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