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