fb4976273610c6b8f20bbf08a90f167205394d25
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / cache / ApplicationDataTypeCache.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 package org.openecomp.sdc.be.model.cache;
22
23 import fj.data.Either;
24 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
25 import org.apache.commons.lang3.tuple.ImmutablePair;
26 import org.openecomp.sdc.be.config.BeEcompErrorManager;
27 import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
28 import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheConfig;
29 import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheInfo;
30 import org.openecomp.sdc.be.config.ConfigurationManager;
31 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
32 import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition;
33 import org.openecomp.sdc.be.model.DataTypeDefinition;
34 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
35 import org.openecomp.sdc.be.resources.data.DataTypeData;
36 import org.openecomp.sdc.common.log.wrappers.Logger;
37 import org.springframework.stereotype.Component;
38
39 import javax.annotation.PostConstruct;
40 import javax.annotation.PreDestroy;
41 import javax.annotation.Resource;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Map.Entry;
46 import java.util.Set;
47 import java.util.concurrent.Executors;
48 import java.util.concurrent.ScheduledExecutorService;
49 import java.util.concurrent.ScheduledFuture;
50 import java.util.concurrent.TimeUnit;
51 import java.util.concurrent.locks.Lock;
52 import java.util.concurrent.locks.ReentrantReadWriteLock;
53 import java.util.stream.Collectors;
54
55 @Component("application-datatype-cache")
56 public class ApplicationDataTypeCache implements ApplicationCache<DataTypeDefinition>, Runnable {
57
58     private static final String APPLICATION_DATA_TYPES_CACHE = "ApplicationDataTypesCache";
59         private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
60     private final Lock r = rwl.readLock();
61     private final Lock w = rwl.writeLock();
62
63     private Map<String, DataTypeDefinition> data = new HashMap<>();
64
65     private ScheduledExecutorService scheduledPollingService = Executors.newScheduledThreadPool(1,
66             new BasicThreadFactory.Builder().namingPattern("ApplicationDataTypeCacheThread-%d").build());
67     ScheduledFuture<?> scheduledFuture = null;
68
69     private static final Logger log = Logger.getLogger(ApplicationDataTypeCache.class.getName());
70
71     private int firstRunDelayInSec = 30;
72     private int pollingIntervalInSec = 60;
73
74     @Resource
75     private PropertyOperation propertyOperation;
76
77     @PostConstruct
78     public void init() {
79
80         ApplicationL1CacheConfig applicationL1CacheConfig = ConfigurationManager.getConfigurationManager()
81                 .getConfiguration().getApplicationL1Cache();
82         if (applicationL1CacheConfig != null) {
83             if (applicationL1CacheConfig.getDatatypes() != null) {
84                 ApplicationL1CacheInfo datatypesInfo = applicationL1CacheConfig.getDatatypes();
85                 if (datatypesInfo.getEnabled()) {
86                     Integer intervalInSec = datatypesInfo.getPollIntervalInSec();
87                     if (intervalInSec != null) {
88                         pollingIntervalInSec = intervalInSec;
89                     }
90                     Integer firstRunDelay = datatypesInfo.getFirstRunDelay();
91                     if (firstRunDelay != null) {
92                         firstRunDelayInSec = firstRunDelay;
93                     }
94                     log.trace("ApplicationDataTypesCache polling interval is {} seconds.", pollingIntervalInSec);
95                     if (scheduledPollingService != null) {
96                         log.debug("Start ApplicationDataTypeCache polling task. polling interval {} seconds",
97                                 pollingIntervalInSec);
98                         scheduledFuture = scheduledPollingService.scheduleAtFixedRate(this, firstRunDelayInSec,
99                                 pollingIntervalInSec, TimeUnit.SECONDS);
100                     }
101
102                 }
103             } else {
104                 BeEcompErrorManager.getInstance().logInternalFlowError(APPLICATION_DATA_TYPES_CACHE, "Cache is disabled",
105                         ErrorSeverity.INFO);
106             }
107         } else {
108             BeEcompErrorManager.getInstance().logInternalFlowError(APPLICATION_DATA_TYPES_CACHE, "Cache is disabled",
109                     ErrorSeverity.INFO);
110         }
111
112     }
113
114     @PreDestroy
115     void destroy() {
116
117         if (scheduledFuture != null) {
118             boolean result = scheduledFuture.cancel(true);
119             log.debug("Stop polling task. result = {}", result);
120
121             scheduledFuture = null;
122         }
123         shutdownExecutor();
124     }
125
126     private void shutdownExecutor() {
127         if (scheduledPollingService == null)
128             return;
129
130         scheduledPollingService.shutdown(); // Disable new tasks from being
131                                             // submitted
132         try {
133             // Wait a while for existing tasks to terminate
134             if (!scheduledPollingService.awaitTermination(60, TimeUnit.SECONDS)) {
135                 scheduledPollingService.shutdownNow(); // Cancel currently
136                                                         // executing tasks
137                 // Wait a while for tasks to respond to being cancelled
138                 if (!scheduledPollingService.awaitTermination(60, TimeUnit.SECONDS))
139                     log.debug("Pool did not terminate");
140             }
141         } catch (InterruptedException ie) {
142             // (Re-)Cancel if current thread also interrupted
143             scheduledPollingService.shutdownNow();
144             // Preserve interrupt status
145             Thread.currentThread().interrupt();
146         }
147     }
148
149     private Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> getAllDataTypesFromGraph() {
150
151         return propertyOperation
152                 .getAllDataTypes();
153
154     }
155
156     @Override
157     public Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> getAll() {
158
159         try {
160
161             r.lock();
162             if (data == null || data.isEmpty()) {
163                 return getAllDataTypesFromGraph();
164             }
165
166             return Either.left(data);
167
168         } finally {
169             r.unlock();
170         }
171     }
172
173     @Override
174     public Either<DataTypeDefinition, JanusGraphOperationStatus> get(String uniqueId) {
175
176         try {
177             r.lock();
178
179             if (data == null || data.isEmpty()) {
180                 return propertyOperation
181                         .getDataTypeByUid(uniqueId);
182             } else {
183                 DataTypeDefinition dataTypeDefinition = data.values().stream()
184                         .filter(p -> p.getUniqueId().equals(uniqueId)).findFirst().orElse(null);
185                 if (dataTypeDefinition == null) {
186                     return propertyOperation
187                             .getDataTypeByUid(uniqueId);
188                 } else {
189                     return Either.left(dataTypeDefinition);
190                 }
191             }
192         } finally {
193             r.unlock();
194         }
195     }
196
197     @Override
198     public void run() {
199         log.trace("run() method. polling db to fetch data types");
200
201         try {
202
203             Long start = System.currentTimeMillis();
204             log.trace("Start fetching all data types from db");
205             Either<List<DataTypeData>, JanusGraphOperationStatus> allDataTypeNodes = propertyOperation.getAllDataTypeNodes();
206             Long end = System.currentTimeMillis();
207             log.trace("Finish fetching all data types from db. Took {} Milliseconds", (end - start));
208             if (allDataTypeNodes.isRight()) {
209                 JanusGraphOperationStatus status = allDataTypeNodes.right().value();
210                 if (status != JanusGraphOperationStatus.OK) {
211                     log.debug("ApplicationDataTypesCache - Failed to fetch all data types nodes");
212                     BeEcompErrorManager.getInstance().logInternalConnectionError("FetchDataTypes",
213                             "Failed to fetch data types from graph(cache)", ErrorSeverity.INFO);
214                 }
215             } else {
216
217                 List<DataTypeData> list = allDataTypeNodes.left().value();
218                 if (list != null) {
219
220                     Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime = list.stream()
221                             .collect(Collectors.toMap(p -> p.getDataTypeDataDefinition().getName(),
222                                     p -> new ImmutablePair<>(p.getDataTypeDataDefinition().getCreationTime(),
223                                             p.getDataTypeDataDefinition().getModificationTime())));
224
225                     Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime = new HashMap<>();
226                     try {
227                         r.lock();
228                         if (data != null) {
229                             currentDataTypeToModificationTime = data.values().stream().collect(Collectors.toMap(
230                                     DataTypeDataDefinition::getName,
231                                     p -> new ImmutablePair<>(p.getCreationTime(), p.getModificationTime())));
232
233                         }
234                     } finally {
235                         r.unlock();
236                     }
237
238                     boolean isChanged = compareDataTypes(dataTypeNameToModificationTime,
239                             currentDataTypeToModificationTime);
240                     if (isChanged) {
241                         replaceAllData();
242                     }
243
244                 }
245             }
246
247         } catch (Exception e) {
248             log.debug("unexpected error occured", e);
249
250             BeEcompErrorManager.getInstance().logInternalUnexpectedError(APPLICATION_DATA_TYPES_CACHE,
251                     "Failed to run refresh data types job", ErrorSeverity.INFO);
252         } finally {
253             try {
254                 propertyOperation.getJanusGraphGenericDao().commit();
255             } catch (Exception e) {
256                 log.trace("Failed to commit ApplicationDataTypeCache", e);
257             }
258         }
259
260     }
261
262     private boolean compareDataTypes(Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime,
263             Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime) {
264         if (dataTypeNameToModificationTime.size() != currentDataTypeToModificationTime.size()) {
265             return true;
266         } else {
267
268             Set<String> currentkeySet = currentDataTypeToModificationTime.keySet();
269             Set<String> keySet = dataTypeNameToModificationTime.keySet();
270
271             if (currentkeySet.containsAll(keySet)) {
272
273                 for (Entry<String, ImmutablePair<Long, Long>> entry : dataTypeNameToModificationTime.entrySet()) {
274                     String dataTypeName = entry.getKey();
275                     ImmutablePair<Long, Long> creationAndModificationTimes = entry.getValue();
276                     long creationTime = creationAndModificationTimes.getLeft() == null ? 0
277                             : creationAndModificationTimes.getLeft().longValue();
278                     long modificationTime = creationAndModificationTimes.getRight() == null ? 0
279                             : creationAndModificationTimes.getRight().longValue();
280
281                     ImmutablePair<Long, Long> currentEntry = currentDataTypeToModificationTime.get(dataTypeName);
282                     long currentCreationTime = currentEntry.getLeft() == null ? 0 : currentEntry.getLeft().longValue();
283                     long currentModificationTime = currentEntry.getRight() == null ? 0
284                             : currentEntry.getRight().longValue();
285
286                     if (creationTime > currentCreationTime || modificationTime > currentModificationTime) {
287                         log.debug("Datatype {} was updated. Creation Time  {} vs {}. Modification Time {} vs {}",
288                                 dataTypeName, currentCreationTime, creationTime, currentModificationTime,
289                                 modificationTime);
290                         return true;
291                     }
292                 }
293             } else {
294                 return true;
295             }
296
297         }
298
299         return false;
300     }
301
302     private void replaceAllData() {
303
304         Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> allDataTypes = propertyOperation
305                 .getAllDataTypes();
306
307         if (allDataTypes.isRight()) {
308             JanusGraphOperationStatus status = allDataTypes.right().value();
309             log.debug("Failed to fetch all data types from db. Status is {}", status);
310         } else {
311
312             try {
313                 w.lock();
314
315                 data = allDataTypes.left().value();
316
317                 BeEcompErrorManager.getInstance().logInternalFlowError("ReplaceDataTypesCache",
318                         "Succeed to replace the data types cache", ErrorSeverity.INFO);
319
320             } finally {
321                 w.unlock();
322             }
323
324         }
325
326     }
327
328 }