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