2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
20 package org.openecomp.sdc.be.model.cache;
22 import fj.data.Either;
23 import java.util.HashMap;
24 import java.util.List;
26 import java.util.Map.Entry;
28 import java.util.concurrent.Executors;
29 import java.util.concurrent.ScheduledExecutorService;
30 import java.util.concurrent.ScheduledFuture;
31 import java.util.concurrent.TimeUnit;
32 import java.util.concurrent.locks.Lock;
33 import java.util.concurrent.locks.ReentrantReadWriteLock;
34 import java.util.stream.Collectors;
35 import javax.annotation.PostConstruct;
36 import javax.annotation.PreDestroy;
37 import javax.annotation.Resource;
39 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
40 import org.apache.commons.lang3.tuple.ImmutablePair;
41 import org.openecomp.sdc.be.config.BeEcompErrorManager;
42 import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
43 import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheConfig;
44 import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheInfo;
45 import org.openecomp.sdc.be.config.ConfigurationManager;
46 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
47 import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition;
48 import org.openecomp.sdc.be.model.DataTypeDefinition;
49 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
50 import org.openecomp.sdc.be.resources.data.DataTypeData;
51 import org.openecomp.sdc.common.log.wrappers.Logger;
52 import org.springframework.beans.factory.annotation.Autowired;
53 import org.springframework.context.ApplicationEvent;
54 import org.springframework.context.ApplicationEventPublisher;
55 import org.springframework.stereotype.Component;
57 @Component("application-datatype-cache")
58 public class ApplicationDataTypeCache implements ApplicationCache<DataTypeDefinition>, Runnable {
60 private static final String APPLICATION_DATA_TYPES_CACHE = "ApplicationDataTypesCache";
61 private static final Logger log = Logger.getLogger(ApplicationDataTypeCache.class.getName());
62 private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
63 private final Lock r = rwl.readLock();
64 private final Lock w = rwl.writeLock();
65 ScheduledFuture<?> scheduledFuture = null;
66 private Map<String, DataTypeDefinition> data = new HashMap<>();
67 private ScheduledExecutorService scheduledPollingService = Executors
68 .newScheduledThreadPool(1, new BasicThreadFactory.Builder().namingPattern("ApplicationDataTypeCacheThread-%d").build());
69 private int firstRunDelayInSec = 30;
70 private int pollingIntervalInSec = 60;
72 private PropertyOperation propertyOperation;
74 private ApplicationEventPublisher applicationEventPublisher;
78 ApplicationL1CacheConfig applicationL1CacheConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getApplicationL1Cache();
79 if (applicationL1CacheConfig != null) {
80 if (applicationL1CacheConfig.getDatatypes() != null) {
81 ApplicationL1CacheInfo datatypesInfo = applicationL1CacheConfig.getDatatypes();
82 if (datatypesInfo.getEnabled()) {
83 Integer intervalInSec = datatypesInfo.getPollIntervalInSec();
84 if (intervalInSec != null) {
85 pollingIntervalInSec = intervalInSec;
87 Integer firstRunDelay = datatypesInfo.getFirstRunDelay();
88 if (firstRunDelay != null) {
89 firstRunDelayInSec = firstRunDelay;
91 log.trace("ApplicationDataTypesCache polling interval is {} seconds.", pollingIntervalInSec);
92 if (scheduledPollingService != null) {
93 log.debug("Start ApplicationDataTypeCache polling task. polling interval {} seconds", pollingIntervalInSec);
94 scheduledFuture = scheduledPollingService
95 .scheduleAtFixedRate(this, firstRunDelayInSec, pollingIntervalInSec, TimeUnit.SECONDS);
99 BeEcompErrorManager.getInstance().logInternalFlowError(APPLICATION_DATA_TYPES_CACHE, "Cache is disabled", ErrorSeverity.INFO);
102 BeEcompErrorManager.getInstance().logInternalFlowError(APPLICATION_DATA_TYPES_CACHE, "Cache is disabled", ErrorSeverity.INFO);
108 if (scheduledFuture != null) {
109 boolean result = scheduledFuture.cancel(true);
110 log.debug("Stop polling task. result = {}", result);
111 scheduledFuture = null;
116 private void shutdownExecutor() {
117 if (scheduledPollingService == null) {
120 scheduledPollingService.shutdown(); // Disable new tasks from being
124 // Wait a while for existing tasks to terminate
125 if (!scheduledPollingService.awaitTermination(60, TimeUnit.SECONDS)) {
126 scheduledPollingService.shutdownNow(); // Cancel currently
130 // Wait a while for tasks to respond to being cancelled
131 if (!scheduledPollingService.awaitTermination(60, TimeUnit.SECONDS)) {
132 log.debug("Pool did not terminate");
135 } catch (InterruptedException ie) {
136 // (Re-)Cancel if current thread also interrupted
137 scheduledPollingService.shutdownNow();
138 // Preserve interrupt status
139 Thread.currentThread().interrupt();
143 private Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> getAllDataTypesFromGraph() {
144 return propertyOperation.getAllDataTypes();
148 public Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> getAll() {
151 if (data == null || data.isEmpty()) {
152 return getAllDataTypesFromGraph();
154 return Either.left(data);
161 public Either<DataTypeDefinition, JanusGraphOperationStatus> get(String uniqueId) {
164 if (data == null || data.isEmpty()) {
165 return propertyOperation.getDataTypeByUid(uniqueId);
167 DataTypeDefinition dataTypeDefinition = data.values().stream().filter(p -> p.getUniqueId().equals(uniqueId)).findFirst().orElse(null);
168 if (dataTypeDefinition == null) {
169 return propertyOperation.getDataTypeByUid(uniqueId);
171 return Either.left(dataTypeDefinition);
181 log.trace("run() method. polling db to fetch data types");
183 Long start = System.currentTimeMillis();
184 log.trace("Start fetching all data types from db");
185 Either<List<DataTypeData>, JanusGraphOperationStatus> allDataTypeNodes = propertyOperation.getAllDataTypeNodes();
186 Long end = System.currentTimeMillis();
187 log.trace("Finish fetching all data types from db. Took {} Milliseconds", (end - start));
188 if (allDataTypeNodes.isRight()) {
189 JanusGraphOperationStatus status = allDataTypeNodes.right().value();
190 if (status != JanusGraphOperationStatus.OK) {
191 log.debug("ApplicationDataTypesCache - Failed to fetch all data types nodes");
192 BeEcompErrorManager.getInstance()
193 .logInternalConnectionError("FetchDataTypes", "Failed to fetch data types from graph(cache)", ErrorSeverity.INFO);
196 List<DataTypeData> list = allDataTypeNodes.left().value();
198 Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime = list.stream().collect(Collectors
199 .toMap(p -> p.getDataTypeDataDefinition().getName(), p -> new ImmutablePair<>(p.getDataTypeDataDefinition().getCreationTime(),
200 p.getDataTypeDataDefinition().getModificationTime())));
201 Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime = new HashMap<>();
205 currentDataTypeToModificationTime = data.values().stream().collect(Collectors
206 .toMap(DataTypeDataDefinition::getName, p -> new ImmutablePair<>(p.getCreationTime(), p.getModificationTime())));
211 boolean isChanged = compareDataTypes(dataTypeNameToModificationTime, currentDataTypeToModificationTime);
217 } catch (Exception e) {
218 log.debug("unexpected error occured", e);
219 BeEcompErrorManager.getInstance()
220 .logInternalUnexpectedError(APPLICATION_DATA_TYPES_CACHE, "Failed to run refresh data types job", ErrorSeverity.INFO);
223 propertyOperation.getJanusGraphGenericDao().commit();
224 } catch (Exception e) {
225 log.trace("Failed to commit ApplicationDataTypeCache", e);
230 private boolean compareDataTypes(Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime,
231 Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime) {
232 if (dataTypeNameToModificationTime.size() != currentDataTypeToModificationTime.size()) {
235 Set<String> currentkeySet = currentDataTypeToModificationTime.keySet();
236 Set<String> keySet = dataTypeNameToModificationTime.keySet();
237 if (currentkeySet.containsAll(keySet)) {
238 for (Entry<String, ImmutablePair<Long, Long>> entry : dataTypeNameToModificationTime.entrySet()) {
239 String dataTypeName = entry.getKey();
240 ImmutablePair<Long, Long> creationAndModificationTimes = entry.getValue();
241 long creationTime = creationAndModificationTimes.getLeft() == null ? 0 : creationAndModificationTimes.getLeft().longValue();
242 long modificationTime = creationAndModificationTimes.getRight() == null ? 0 : creationAndModificationTimes.getRight().longValue();
243 ImmutablePair<Long, Long> currentEntry = currentDataTypeToModificationTime.get(dataTypeName);
244 long currentCreationTime = currentEntry.getLeft() == null ? 0 : currentEntry.getLeft().longValue();
245 long currentModificationTime = currentEntry.getRight() == null ? 0 : currentEntry.getRight().longValue();
246 if (creationTime > currentCreationTime || modificationTime > currentModificationTime) {
247 log.debug("Datatype {} was updated. Creation Time {} vs {}. Modification Time {} vs {}", dataTypeName, currentCreationTime,
248 creationTime, currentModificationTime, modificationTime);
259 private void replaceAllData() {
260 Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> allDataTypes = propertyOperation.getAllDataTypes();
261 if (allDataTypes.isRight()) {
262 JanusGraphOperationStatus status = allDataTypes.right().value();
263 log.debug("Failed to fetch all data types from db. Status is {}", status);
267 data = allDataTypes.left().value();
268 // send notification on data types change
269 onDataChangeEventEmit(data);
270 BeEcompErrorManager.getInstance()
271 .logInternalFlowError("ReplaceDataTypesCache", "Succeed to replace the data types cache", ErrorSeverity.INFO);
278 private void onDataChangeEventEmit(Map<String, DataTypeDefinition> newData) {
279 log.trace("Cache data has changed, sending event to all listening for this change.");
280 DataTypesCacheChangedEvent dataTypesCacheChangedEvent = new DataTypesCacheChangedEvent(this, newData);
281 applicationEventPublisher.publishEvent(dataTypesCacheChangedEvent);
285 * Custom event to notify all interested in cached data changes
287 public static class DataTypesCacheChangedEvent extends ApplicationEvent {
290 private Map<String, DataTypeDefinition> newData;
292 public DataTypesCacheChangedEvent(Object source, Map<String, DataTypeDefinition> newData) {
294 this.newData = newData;