Fix Blocker/Critical sonar issues
[aai/sparky-be.git] / src / main / java / org / openecomp / sparky / viewandinspect / services / VisualizationService.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.viewandinspect.services;
24
25 import java.io.IOException;
26 import java.security.SecureRandom;
27 import java.util.Map;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ExecutorService;
30
31 import javax.servlet.ServletException;
32
33 import org.openecomp.cl.api.Logger;
34 import org.openecomp.cl.eelf.LoggerFactory;
35 import org.openecomp.sparky.config.oxm.OxmModelLoader;
36 import org.openecomp.sparky.dal.aai.ActiveInventoryAdapter;
37 import org.openecomp.sparky.dal.aai.ActiveInventoryDataProvider;
38 import org.openecomp.sparky.dal.aai.config.ActiveInventoryConfig;
39 import org.openecomp.sparky.dal.aai.config.ActiveInventoryRestConfig;
40 import org.openecomp.sparky.dal.cache.EntityCache;
41 import org.openecomp.sparky.dal.cache.PersistentEntityCache;
42 import org.openecomp.sparky.dal.elasticsearch.ElasticSearchAdapter;
43 import org.openecomp.sparky.dal.elasticsearch.ElasticSearchDataProvider;
44 import org.openecomp.sparky.dal.elasticsearch.config.ElasticSearchConfig;
45 import org.openecomp.sparky.dal.rest.OperationResult;
46 import org.openecomp.sparky.dal.rest.RestClientBuilder;
47 import org.openecomp.sparky.dal.rest.RestfulDataAccessor;
48 import org.openecomp.sparky.logging.AaiUiMsgs;
49 import org.openecomp.sparky.synchronizer.entity.SearchableEntity;
50 import org.openecomp.sparky.util.NodeUtils;
51 import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
52 import org.openecomp.sparky.viewandinspect.entity.ActiveInventoryNode;
53 import org.openecomp.sparky.viewandinspect.entity.D3VisualizationOutput;
54 import org.openecomp.sparky.viewandinspect.entity.GraphMeta;
55 import org.openecomp.sparky.viewandinspect.entity.QueryParams;
56 import org.openecomp.sparky.viewandinspect.entity.QueryRequest;
57
58 import com.fasterxml.jackson.annotation.JsonInclude.Include;
59 import com.fasterxml.jackson.core.JsonProcessingException;
60 import com.fasterxml.jackson.databind.DeserializationFeature;
61 import com.fasterxml.jackson.databind.JsonNode;
62 import com.fasterxml.jackson.databind.ObjectMapper;
63
64 public class VisualizationService {
65   
66   private static final Logger LOG =
67       LoggerFactory.getInstance().getLogger(VisualizationService.class);
68   
69   private OxmModelLoader loader;
70   private ObjectMapper mapper = new ObjectMapper();
71
72   private final ActiveInventoryDataProvider aaiProvider;
73   private final ActiveInventoryRestConfig aaiRestConfig;
74   private final ElasticSearchDataProvider esProvider;
75   private final ElasticSearchConfig esConfig;
76   private final ExecutorService aaiExecutorService;
77   
78   private ConcurrentHashMap<Long, VisualizationContext> contextMap;
79   private final SecureRandom secureRandom;
80   
81   private ActiveInventoryConfig aaiConfig;
82   private VisualizationConfig visualizationConfig;
83   
84   public VisualizationService(OxmModelLoader loader) throws Exception {
85     this.loader = loader;
86
87     aaiRestConfig = ActiveInventoryConfig.getConfig().getAaiRestConfig();
88
89     EntityCache cache = null;
90     secureRandom = new SecureRandom();
91
92     ActiveInventoryAdapter aaiAdapter = new ActiveInventoryAdapter(new RestClientBuilder());
93
94     if (aaiRestConfig.isCacheEnabled()) {
95       cache = new PersistentEntityCache(aaiRestConfig.getStorageFolderOverride(),
96           aaiRestConfig.getNumCacheWorkers());
97
98       aaiAdapter.setCacheEnabled(true);
99       aaiAdapter.setEntityCache(cache);
100     }
101
102     this.aaiProvider = aaiAdapter;
103
104     RestClientBuilder esClientBuilder = new RestClientBuilder();
105     esClientBuilder.setUseHttps(false);
106     RestfulDataAccessor nonCachingRestProvider = new RestfulDataAccessor(esClientBuilder);
107     this.esConfig = ElasticSearchConfig.getConfig();
108     this.esProvider = new ElasticSearchAdapter(nonCachingRestProvider, this.esConfig);
109     
110     this.mapper = new ObjectMapper();
111     mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
112     
113     this.contextMap = new ConcurrentHashMap<Long, VisualizationContext>();
114     this.visualizationConfig = VisualizationConfig.getConfig();
115     this.aaiConfig = ActiveInventoryConfig.getConfig();
116     this.aaiExecutorService = NodeUtils.createNamedExecutor("SLNC-WORKER",
117         aaiConfig.getAaiRestConfig().getNumResolverWorkers(), LOG);
118   }
119   
120   public OxmModelLoader getLoader() {
121     return loader;
122   }
123
124   public void setLoader(OxmModelLoader loader) {
125     this.loader = loader;
126   }
127   
128   /**
129    * Analyze query request body.
130    *
131    * @param queryRequestJson the query request json
132    * @return the query request
133    */
134
135   public QueryRequest analyzeQueryRequestBody(String queryRequestJson) {
136
137
138     LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
139         "analyzeQueryRequestBody()," + " queryRequestJson = " + queryRequestJson);
140
141     ObjectMapper nonEmptyMapper = new ObjectMapper();
142     nonEmptyMapper.setSerializationInclusion(Include.NON_EMPTY);
143
144     QueryRequest queryBody = null;
145
146     try {
147       queryBody = nonEmptyMapper.readValue(queryRequestJson, QueryRequest.class);
148     } catch (Exception exc) {
149       LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, "Analyzing query request body.",
150           exc.getLocalizedMessage());
151     }
152
153     return queryBody;
154
155   }
156   
157   /**
158    * Log optime.
159    *
160    * @param method the method
161    * @param opStartTimeInMs the op start time in ms
162    */
163   private void logOptime(String method, long opStartTimeInMs) {
164     LOG.info(AaiUiMsgs.OPERATION_TIME, method,
165         String.valueOf(System.currentTimeMillis() - opStartTimeInMs));
166   }
167   
168   private SearchableEntity extractSearchableEntityFromElasticEntity(OperationResult operationResult) {
169     if (operationResult == null || !operationResult.wasSuccessful()) {
170       // error, return empty collection
171       return null;
172     }
173  
174     SearchableEntity sourceEntity = null;
175     if (operationResult.wasSuccessful()) {
176
177       try {
178         JsonNode elasticValue = mapper.readValue(operationResult.getResult(), JsonNode.class);
179
180         if (elasticValue != null) {
181           JsonNode sourceField = elasticValue.get("_source");
182
183           if (sourceField != null) {
184             sourceEntity = new SearchableEntity();
185             
186             String entityType = NodeUtils.extractFieldValueFromObject(sourceField, "entityType"); 
187             sourceEntity.setEntityType(entityType);  
188             String entityPrimaryKeyValue = NodeUtils.extractFieldValueFromObject(sourceField, "entityPrimaryKeyValue");
189             sourceEntity.setEntityPrimaryKeyValue(entityPrimaryKeyValue);
190             String link = NodeUtils.extractFieldValueFromObject(sourceField, "link"); 
191             sourceEntity.setLink(link);
192             String lastmodTimestamp = NodeUtils.extractFieldValueFromObject(sourceField, "lastmodTimestamp");
193             sourceEntity.setEntityTimeStamp(lastmodTimestamp);
194           }
195         }
196       } catch (IOException ioe) {
197         LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "a json node ", ioe.getLocalizedMessage());
198       }
199     }
200     return sourceEntity;
201   }
202
203   /**
204    * Builds the visualization using generic query.
205    *
206    * @param queryRequest the query request
207    * @return the operation result
208    */
209   public OperationResult buildVisualizationUsingGenericQuery(QueryRequest queryRequest) {
210
211     OperationResult returnValue = new OperationResult();
212     OperationResult dataCollectionResult = null;
213     QueryParams queryParams = null;
214     SearchableEntity sourceEntity = null;
215
216     try {
217
218       /*
219        * Here is where we need to make a dip to elastic-search for the self-link by entity-id (link
220        * hash).
221        */
222       dataCollectionResult = esProvider.retrieveEntityById(queryRequest.getHashId());
223       sourceEntity = extractSearchableEntityFromElasticEntity(dataCollectionResult);
224
225       if (sourceEntity != null) {
226         sourceEntity.generateId();
227       }
228
229       queryParams = new QueryParams();
230       queryParams.setSearchTargetNodeId(queryRequest.getHashId());
231
232     } catch (Exception e1) {
233       LOG.error(AaiUiMsgs.FAILED_TO_GET_NODES_QUERY_RESULT, e1.getLocalizedMessage());
234       dataCollectionResult = new OperationResult(500, "Failed to get nodes-query result from AAI");
235     }
236
237     if (dataCollectionResult.getResultCode() == 200) {
238
239       String d3OutputJsonOutput = null;
240
241       try {
242
243         d3OutputJsonOutput = getVisualizationOutputBasedonGenericQuery(sourceEntity, queryParams);
244
245         if (LOG.isDebugEnabled()) {
246           LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
247               "Generated D3" + " output as json = " + d3OutputJsonOutput);
248         }
249
250         if (d3OutputJsonOutput != null) {
251           returnValue.setResultCode(200);
252           returnValue.setResult(d3OutputJsonOutput);
253         } else {
254           returnValue.setResult(500, "Failed to generate D3 graph visualization");
255         }
256
257       } catch (Exception exc) {
258         returnValue.setResult(500,
259             "Failed to generate D3 graph visualization, due to a servlet exception.");
260         LOG.error(AaiUiMsgs.ERROR_D3_GRAPH_VISUALIZATION, exc.getLocalizedMessage());
261       }
262     } else {
263       returnValue.setResult(dataCollectionResult.getResultCode(), dataCollectionResult.getResult());
264     }
265
266     return returnValue;
267
268   }
269   
270   /**
271    * Gets the visualization output basedon generic query.
272    *
273    * @param searchtargetEntity entity that will be used to start visualization flow
274    * @param queryParams the query params
275    * @return the visualization output basedon generic query
276    * @throws ServletException the servlet exception
277    */
278   private String getVisualizationOutputBasedonGenericQuery(SearchableEntity searchtargetEntity,
279       QueryParams queryParams) throws ServletException {
280
281     long opStartTimeInMs = System.currentTimeMillis();
282
283     VisualizationTransformer transformer = null;
284     try {
285       transformer = new VisualizationTransformer();
286     } catch (Exception exc) {
287       throw new ServletException(
288           "Failed to create VisualizationTransformer instance because of execption", exc);
289     }
290
291     VisualizationContext visContext = null;
292     long contextId = secureRandom.nextLong();
293     try {
294       visContext = new VisualizationContext(contextId, aaiProvider, aaiExecutorService, loader);
295       contextMap.putIfAbsent(contextId, visContext);
296     } catch (Exception e1) {
297       LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT,
298           "While building Visualization Context, " + e1.getLocalizedMessage());
299       throw new ServletException(e1);
300     }
301
302     String jsonResponse = null;
303
304     long startTimeInMs = System.currentTimeMillis();
305
306     visContext.processSelfLinks(searchtargetEntity, queryParams);
307     contextMap.remove(contextId);
308
309     logOptime("collectSelfLinkNodes()", startTimeInMs);
310
311     /*
312      * Flatten the graphs into a set of Graph and Link nodes. In this method I want the node graph
313      * resulting from the edge-tag-query to be represented first, and then we'll layer in
314      * relationship data.
315      */
316     long overlayDataStartTimeInMs = System.currentTimeMillis();
317
318     Map<String, ActiveInventoryNode> cachedNodeMap = visContext.getNodeCache();
319
320     if (LOG.isDebugEnabled()) {
321
322       StringBuilder sb = new StringBuilder(128);
323
324       sb.append("\nCached Node Map:\n");
325       for (String k : cachedNodeMap.keySet()) {
326         sb.append("\n----");
327         sb.append("\n").append(cachedNodeMap.get(k).dumpNodeTree(true));
328       }
329
330       LOG.debug(AaiUiMsgs.DEBUG_GENERIC, sb.toString());
331     }
332
333     transformer.buildFlatNodeArrayFromGraphCollection(cachedNodeMap);
334     transformer.buildLinksFromGraphCollection(cachedNodeMap);
335
336     /*
337      * - Apply configuration-driven styling
338      * - Build the final transformation response object
339      * - Use information we have to populate the GraphMeta object
340      */
341
342     transformer.addSearchTargetAttributesToRootNode();
343     
344     GraphMeta graphMeta = new GraphMeta();
345
346     D3VisualizationOutput output = null;
347     try {
348       output = transformer
349           .generateVisualizationOutput((System.currentTimeMillis() - opStartTimeInMs), graphMeta);
350     } catch (Exception exc) {
351       LOG.error(AaiUiMsgs.FAILURE_TO_PROCESS_REQUEST, exc.getLocalizedMessage());
352       throw new ServletException("Caught an exception while generation visualization output", exc);
353     }
354
355     output.setInlineMessage(visContext.getInlineMessage());
356     output.getGraphMeta().setNumLinkResolveFailed(visContext.getNumFailedLinkResolve());
357     output.getGraphMeta().setNumLinksResolvedSuccessfullyFromCache(
358         visContext.getNumSuccessfulLinkResolveFromCache());
359     output.getGraphMeta().setNumLinksResolvedSuccessfullyFromServer(
360         visContext.getNumSuccessfulLinkResolveFromFromServer());
361
362     try {
363       jsonResponse = transformer.convertVisualizationOutputToJson(output);
364     } catch (JsonProcessingException jpe) {
365       throw new ServletException(
366           "Caught an exception while converting visualization output to json", jpe);
367     }
368
369     logOptime("[build flat node array, add relationship data, search target,"
370         + " color scheme, and generate visualization output]", overlayDataStartTimeInMs);
371     
372     logOptime("doFilter()", opStartTimeInMs);
373
374     return jsonResponse;
375
376   }
377   
378   public void shutdown() {
379     aaiProvider.shutdown();
380     aaiExecutorService.shutdown();
381     esProvider.shutdown();
382   }
383 }