Fix potential null pointer places
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / viewandinspect / services / BaseVisualizationService.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 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 package org.onap.aai.sparky.viewandinspect.services;
22
23 import java.io.IOException;
24 import java.security.SecureRandom;
25 import java.util.Map;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ExecutorService;
28
29 import javax.servlet.ServletException;
30
31 import org.onap.aai.cl.api.Logger;
32 import org.onap.aai.cl.eelf.LoggerFactory;
33 import org.onap.aai.restclient.client.OperationResult;
34 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
35 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
36 import org.onap.aai.sparky.dal.ActiveInventoryAdapter;
37 import org.onap.aai.sparky.dal.ElasticSearchAdapter;
38 import org.onap.aai.sparky.dal.GizmoAdapter;
39 import org.onap.aai.sparky.logging.AaiUiMsgs;
40 import org.onap.aai.sparky.subscription.config.SubscriptionConfig;
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 BaseVisualizationService implements VisualizationService {
59   
60   private static final Logger LOG =
61       LoggerFactory.getInstance().getLogger(BaseVisualizationService.class);
62   
63   private ObjectMapper mapper = new ObjectMapper();
64
65   private final ActiveInventoryAdapter aaiAdapter;
66   private final GizmoAdapter gizmoAdapter;
67   private final ElasticSearchAdapter esAdapter;
68   private final ExecutorService aaiExecutorService;
69   
70   private ConcurrentHashMap<Long, VisualizationContext> contextMap;
71   private final SecureRandom secureRandom;
72
73   private VisualizationConfigs visualizationConfigs;
74   private SubscriptionConfig subConfig;
75   private ElasticSearchEndpointConfig endpointEConfig;
76   private ElasticSearchSchemaConfig schemaEConfig;
77   private OxmEntityLookup oxmEntityLookup;
78   
79         public BaseVisualizationService(OxmModelLoader loader, VisualizationConfigs visualizationConfigs,
80                         ActiveInventoryAdapter aaiAdapter, GizmoAdapter gizmoAdapter, ElasticSearchAdapter esAdapter,
81                         ElasticSearchEndpointConfig endpointConfig, ElasticSearchSchemaConfig schemaConfig,
82                         int numActiveInventoryWorkers, OxmEntityLookup oxmEntityLookup, SubscriptionConfig subscriptionConfig)
83                         throws Exception {
84    
85     this.visualizationConfigs = visualizationConfigs;
86     this.endpointEConfig = endpointConfig; 
87     this.schemaEConfig = schemaConfig; 
88     this.oxmEntityLookup = oxmEntityLookup;
89     this.subConfig = subscriptionConfig;
90     
91
92     secureRandom = new SecureRandom();
93     
94     /*
95      * Fix constructor with properly wired in properties
96      */
97  
98     this.aaiAdapter = aaiAdapter;
99     this.gizmoAdapter = gizmoAdapter;
100     this.esAdapter = esAdapter; 
101
102     this.mapper = new ObjectMapper();
103     mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
104     
105     this.contextMap = new ConcurrentHashMap<Long, VisualizationContext>();
106     
107     this.aaiExecutorService = NodeUtils.createNamedExecutor("SLNC-WORKER",
108         numActiveInventoryWorkers, LOG);
109     
110   }
111   
112   /**
113    * Analyze query request body.
114    *
115    * @param queryRequestJson the query request json
116    * @return the query request
117    */
118
119   public QueryRequest analyzeQueryRequestBody(String queryRequestJson) {
120
121
122     LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
123         "analyzeQueryRequestBody()," + " queryRequestJson = " + queryRequestJson);
124
125     ObjectMapper nonEmptyMapper = new ObjectMapper();
126     nonEmptyMapper.setSerializationInclusion(Include.NON_EMPTY);
127
128     QueryRequest queryBody = null;
129
130     try {
131       queryBody = nonEmptyMapper.readValue(queryRequestJson, QueryRequest.class);
132     } catch (Exception exc) {
133       LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, "Analyzing query request body.",
134           exc.getLocalizedMessage());
135     }
136
137     return queryBody;
138
139   }
140   
141   /**
142    * Log optime.
143    *
144    * @param method the method
145    * @param opStartTimeInMs the op start time in ms
146    */
147   private void logOptime(String method, long opStartTimeInMs) {
148     LOG.info(AaiUiMsgs.OPERATION_TIME, method,
149         String.valueOf(System.currentTimeMillis() - opStartTimeInMs));
150   }
151   
152   private SearchableEntity extractSearchableEntityFromElasticEntity(OperationResult operationResult) {
153     if (operationResult == null || !operationResult.wasSuccessful()) {
154       // error, return empty collection
155       return null;
156     }
157  
158     SearchableEntity sourceEntity = null;
159     if (operationResult.wasSuccessful()) {
160
161       try {
162         JsonNode elasticValue = mapper.readValue(operationResult.getResult(), JsonNode.class);
163
164         if (elasticValue != null) {
165           JsonNode sourceField = elasticValue.get("_source");
166
167           if (sourceField != null) {
168             sourceEntity = new SearchableEntity();
169             
170             String entityType = NodeUtils.extractFieldValueFromObject(sourceField, "entityType"); 
171             sourceEntity.setEntityType(entityType);  
172             String entityPrimaryKeyValue = NodeUtils.extractFieldValueFromObject(sourceField, "entityPrimaryKeyValue");
173             sourceEntity.setEntityPrimaryKeyValue(entityPrimaryKeyValue);
174             String link = NodeUtils.extractFieldValueFromObject(sourceField, "link"); 
175             sourceEntity.setLink(link);
176             String lastmodTimestamp = 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 = new OperationResult();
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 = getVisualizationOutputBasedonGenericQuery( sourceEntity, queryParams, queryRequest);
230
231         if (LOG.isDebugEnabled()) {
232           LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
233               "Generated D3" + " output as json = " + d3OutputJsonOutput);
234         }
235
236         if (d3OutputJsonOutput != null) {
237           returnValue.setResultCode(200);
238           returnValue.setResult(d3OutputJsonOutput);
239         } else {
240           returnValue.setResult(500, "Failed to generate D3 graph visualization");
241         }
242
243       } catch (Exception exc) {
244         returnValue.setResult(500,
245             "Failed to generate D3 graph visualization, due to a servlet exception.");
246         LOG.error(AaiUiMsgs.ERROR_D3_GRAPH_VISUALIZATION, exc.getLocalizedMessage());
247       }
248     } else {
249       returnValue.setResult(dataCollectionResult.getResultCode(), dataCollectionResult.getResult());
250     }
251
252     return returnValue;
253
254   }
255   
256   
257   /**
258    * Gets the visualization output basedon generic query.
259    *
260    * @param searchtargetEntity entity that will be used to start visualization flow
261    * @param queryParams the query params
262    * @return the visualization output basedon generic query
263    * @throws ServletException the servlet exception
264    * @throws  
265    */
266   private String getVisualizationOutputBasedonGenericQuery(SearchableEntity searchtargetEntity,
267       QueryParams queryParams, QueryRequest request) throws ServletException {
268
269     long opStartTimeInMs = System.currentTimeMillis();
270
271     VisualizationTransformer transformer = null;
272     try {
273       transformer = new VisualizationTransformer(visualizationConfigs, subConfig);
274     } catch (Exception exc) {
275       throw new ServletException(
276           "Failed to create VisualizationTransformer instance because of execption", exc);
277     }
278
279     VisualizationContext visContext = null;
280     long contextId = secureRandom.nextLong();
281     try {
282         if ( visualizationConfigs.isGizmoEnabled()) {
283               visContext = new BaseGizmoVisualizationContext(contextId, this.gizmoAdapter, aaiExecutorService,
284                       this.visualizationConfigs, oxmEntityLookup);
285         } else {
286               visContext = new BaseVisualizationContext(contextId, this.aaiAdapter, aaiExecutorService,
287                       this.visualizationConfigs, oxmEntityLookup);
288         }
289         
290       contextMap.putIfAbsent(contextId, visContext);
291     } catch (Exception e1) {
292       LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT,
293           "While building Visualization Context, " + e1.getLocalizedMessage());
294       throw new ServletException(e1);
295     }
296
297     long startTimeInMs = System.currentTimeMillis();
298
299     visContext.processSelfLinks(searchtargetEntity, queryParams);
300     contextMap.remove(contextId);
301
302     logOptime("collectSelfLinkNodes()", startTimeInMs);
303
304     /*
305      * Flatten the graphs into a set of Graph and Link nodes. In this method I want the node graph
306      * resulting from the edge-tag-query to be represented first, and then we'll layer in
307      * relationship data.
308      */
309     long overlayDataStartTimeInMs = System.currentTimeMillis();
310
311     Map<String, ActiveInventoryNode> cachedNodeMap = visContext.getNodeCache();
312
313     if (LOG.isDebugEnabled()) {
314
315       StringBuilder sb = new StringBuilder(128);
316
317       sb.append("\nCached Node Map:\n");
318       for (String k : cachedNodeMap.keySet()) {
319         sb.append("\n----");
320         sb.append("\n").append(cachedNodeMap.get(k).dumpNodeTree(true));
321       }
322
323       LOG.debug(AaiUiMsgs.DEBUG_GENERIC, sb.toString());
324     }
325
326     transformer.buildFlatNodeArrayFromGraphCollection(cachedNodeMap);
327     transformer.buildLinksFromGraphCollection(cachedNodeMap);
328
329     /*
330      * - Apply configuration-driven styling
331      * - Build the final transformation response object
332      * - Use information we have to populate the GraphMeta object
333      */
334
335     transformer.addSearchTargetAttributesToRootNode();
336     
337     GraphMeta graphMeta = new GraphMeta();
338
339     D3VisualizationOutput output = getD3VisualizationOutput(opStartTimeInMs, transformer, graphMeta);
340
341     String jsonResponse = null;
342
343     if (output != null) {
344       output.setInlineMessage(visContext.getInlineMessage());
345       output.getGraphMeta().setNumLinkResolveFailed(visContext.getNumFailedLinkResolve());
346       output.getGraphMeta().setNumLinksResolvedSuccessfullyFromCache(
347               visContext.getNumSuccessfulLinkResolveFromCache());
348       output.getGraphMeta().setNumLinksResolvedSuccessfullyFromServer(
349               visContext.getNumSuccessfulLinkResolveFromFromServer());
350
351       try {
352         jsonResponse = transformer.convertVisualizationOutputToJson(output);
353       } catch (JsonProcessingException jpe) {
354         throw new ServletException(
355                 "Caught an exception while converting visualization output to json", jpe);
356       }
357     }
358
359     logOptime("[build flat node array, add relationship data, search target,"
360         + " color scheme, and generate visualization output]", overlayDataStartTimeInMs);
361     
362     logOptime("doFilter()", opStartTimeInMs);
363
364     return jsonResponse;
365
366   }
367
368   private D3VisualizationOutput getD3VisualizationOutput(long opStartTimeInMs, VisualizationTransformer transformer, GraphMeta graphMeta) throws ServletException {
369     D3VisualizationOutput output = null;
370     try {
371       output = transformer
372           .generateVisualizationOutput((System.currentTimeMillis() - opStartTimeInMs), graphMeta);
373     } catch (JsonProcessingException exc) {
374       throw new ServletException("Caught an exception while generation visualization output", exc);
375     } catch (IOException exc) {
376       LOG.error(AaiUiMsgs.FAILURE_TO_PROCESS_REQUEST, exc.getLocalizedMessage());
377     }
378     return output;
379   }
380
381   public void shutdown() {
382     aaiExecutorService.shutdown();
383   }
384   
385 }