Adding interfaces in documentation
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / viewandinspect / services / BaseVisualizationService.java
1 /**
2  * ============LICENSE_START===================================================
3  * SPARKY (AAI UI service)
4  * ============================================================================
5  * Copyright © 2017 AT&T Intellectual Property.
6  * Copyright © 2017 Amdocs
7  * All rights reserved.
8  * ============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=====================================================
21  *
22  * ECOMP and OpenECOMP are trademarks
23  * and service marks of AT&T Intellectual Property.
24  */
25 package org.onap.aai.sparky.viewandinspect.services;
26
27 import java.io.IOException;
28 import java.security.SecureRandom;
29 import java.util.Map;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ExecutorService;
32
33 import javax.servlet.ServletException;
34
35 import org.onap.aai.cl.api.Logger;
36 import org.onap.aai.cl.eelf.LoggerFactory;
37 import org.onap.aai.restclient.client.OperationResult;
38 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
39 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
40 import org.onap.aai.sparky.dal.ActiveInventoryAdapter;
41 import org.onap.aai.sparky.dal.ElasticSearchAdapter;
42 import org.onap.aai.sparky.dal.GizmoAdapter;
43 import org.onap.aai.sparky.logging.AaiUiMsgs;
44 import org.onap.aai.sparky.subscription.config.SubscriptionConfig;
45 import org.onap.aai.sparky.sync.config.ElasticSearchEndpointConfig;
46 import org.onap.aai.sparky.sync.config.ElasticSearchSchemaConfig;
47 import org.onap.aai.sparky.sync.entity.SearchableEntity;
48 import org.onap.aai.sparky.util.NodeUtils;
49 import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs;
50 import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode;
51 import org.onap.aai.sparky.viewandinspect.entity.D3VisualizationOutput;
52 import org.onap.aai.sparky.viewandinspect.entity.GraphMeta;
53 import org.onap.aai.sparky.viewandinspect.entity.QueryParams;
54 import org.onap.aai.sparky.viewandinspect.entity.QueryRequest;
55
56 import com.fasterxml.jackson.annotation.JsonInclude.Include;
57 import com.fasterxml.jackson.core.JsonProcessingException;
58 import com.fasterxml.jackson.databind.DeserializationFeature;
59 import com.fasterxml.jackson.databind.JsonNode;
60 import com.fasterxml.jackson.databind.ObjectMapper;
61
62 public class BaseVisualizationService implements VisualizationService {
63   
64   private static final Logger LOG =
65       LoggerFactory.getInstance().getLogger(BaseVisualizationService.class);
66   
67   private ObjectMapper mapper = new ObjectMapper();
68
69   private final ActiveInventoryAdapter aaiAdapter;
70   private final GizmoAdapter gizmoAdapter;
71   private final ElasticSearchAdapter esAdapter;
72   private final ExecutorService aaiExecutorService;
73   
74   private ConcurrentHashMap<Long, VisualizationContext> contextMap;
75   private final SecureRandom secureRandom;
76
77   private VisualizationConfigs visualizationConfigs;
78   private SubscriptionConfig subConfig;
79   private ElasticSearchEndpointConfig endpointEConfig;
80   private ElasticSearchSchemaConfig schemaEConfig;
81   private OxmEntityLookup oxmEntityLookup;
82   
83         public BaseVisualizationService(OxmModelLoader loader, VisualizationConfigs visualizationConfigs,
84                         ActiveInventoryAdapter aaiAdapter, GizmoAdapter gizmoAdapter, ElasticSearchAdapter esAdapter,
85                         ElasticSearchEndpointConfig endpointConfig, ElasticSearchSchemaConfig schemaConfig,
86                         int numActiveInventoryWorkers, OxmEntityLookup oxmEntityLookup, SubscriptionConfig subscriptionConfig)
87                         throws Exception {
88    
89     this.visualizationConfigs = visualizationConfigs;
90     this.endpointEConfig = endpointConfig; 
91     this.schemaEConfig = schemaConfig; 
92     this.oxmEntityLookup = oxmEntityLookup;
93     this.subConfig = subscriptionConfig;
94     
95
96     secureRandom = new SecureRandom();
97     
98     /*
99      * Fix constructor with properly wired in properties
100      */
101  
102     this.aaiAdapter = aaiAdapter;
103     this.gizmoAdapter = gizmoAdapter;
104     this.esAdapter = esAdapter; 
105
106     this.mapper = new ObjectMapper();
107     mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
108     
109     this.contextMap = new ConcurrentHashMap<Long, VisualizationContext>();
110     
111     this.aaiExecutorService = NodeUtils.createNamedExecutor("SLNC-WORKER",
112         numActiveInventoryWorkers, LOG);
113     
114   }
115   
116   /**
117    * Analyze query request body.
118    *
119    * @param queryRequestJson the query request json
120    * @return the query request
121    */
122
123   public QueryRequest analyzeQueryRequestBody(String queryRequestJson) {
124
125
126     LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
127         "analyzeQueryRequestBody()," + " queryRequestJson = " + queryRequestJson);
128
129     ObjectMapper nonEmptyMapper = new ObjectMapper();
130     nonEmptyMapper.setSerializationInclusion(Include.NON_EMPTY);
131
132     QueryRequest queryBody = null;
133
134     try {
135       queryBody = nonEmptyMapper.readValue(queryRequestJson, QueryRequest.class);
136     } catch (Exception exc) {
137       LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, "Analyzing query request body.",
138           exc.getLocalizedMessage());
139     }
140
141     return queryBody;
142
143   }
144   
145   /**
146    * Log optime.
147    *
148    * @param method the method
149    * @param opStartTimeInMs the op start time in ms
150    */
151   private void logOptime(String method, long opStartTimeInMs) {
152     LOG.info(AaiUiMsgs.OPERATION_TIME, method,
153         String.valueOf(System.currentTimeMillis() - opStartTimeInMs));
154   }
155   
156   private SearchableEntity extractSearchableEntityFromElasticEntity(OperationResult operationResult) {
157     if (operationResult == null || !operationResult.wasSuccessful()) {
158       // error, return empty collection
159       return null;
160     }
161  
162     SearchableEntity sourceEntity = null;
163     if (operationResult.wasSuccessful()) {
164
165       try {
166         JsonNode elasticValue = mapper.readValue(operationResult.getResult(), JsonNode.class);
167
168         if (elasticValue != null) {
169           JsonNode sourceField = elasticValue.get("_source");
170
171           if (sourceField != null) {
172             sourceEntity = new SearchableEntity();
173             
174             String entityType = NodeUtils.extractFieldValueFromObject(sourceField, "entityType"); 
175             sourceEntity.setEntityType(entityType);  
176             String entityPrimaryKeyValue = NodeUtils.extractFieldValueFromObject(sourceField, "entityPrimaryKeyValue");
177             sourceEntity.setEntityPrimaryKeyValue(entityPrimaryKeyValue);
178             String link = NodeUtils.extractFieldValueFromObject(sourceField, "link"); 
179             sourceEntity.setLink(link);
180             String lastmodTimestamp = NodeUtils.extractFieldValueFromObject(sourceField, "lastmodTimestamp");
181             sourceEntity.setEntityTimeStamp(lastmodTimestamp);
182           }
183         }
184       } catch (IOException ioe) {
185         LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "a json node ", ioe.getLocalizedMessage());
186       }
187     }
188     return sourceEntity;
189   }
190
191   /**
192    * Builds the visualization using generic query.
193    *
194    * @param queryRequest the query request
195    * @return the operation result
196    */
197   public OperationResult buildVisualizationUsingGenericQuery(QueryRequest queryRequest) {
198
199     OperationResult returnValue = new OperationResult();
200     OperationResult dataCollectionResult = null;
201     QueryParams queryParams = null;
202     SearchableEntity sourceEntity = null;
203
204     try {
205
206       /*
207        * Here is where we need to make a dip to elastic-search for the self-link by entity-id (link
208        * hash).
209        */
210       dataCollectionResult = esAdapter.retrieveEntityById(endpointEConfig.getEsIpAddress(), 
211                   endpointEConfig.getEsServerPort(),schemaEConfig.getIndexName(),
212                   schemaEConfig.getIndexDocType(), queryRequest.getHashId());
213       sourceEntity = extractSearchableEntityFromElasticEntity(dataCollectionResult);
214
215       if (sourceEntity != null) {
216         sourceEntity.generateId();
217       }
218
219       queryParams = new QueryParams();
220       queryParams.setSearchTargetNodeId(queryRequest.getHashId());
221
222     } catch (Exception e1) {
223       LOG.error(AaiUiMsgs.FAILED_TO_GET_NODES_QUERY_RESULT, e1.getLocalizedMessage());
224       dataCollectionResult = new OperationResult(500, "Failed to get nodes-query result from AAI");
225     }
226
227     if (dataCollectionResult.getResultCode() == 200) {
228
229       String d3OutputJsonOutput = null;
230
231       try {
232
233         d3OutputJsonOutput = getVisualizationOutputBasedonGenericQuery( sourceEntity, queryParams, queryRequest);
234
235         if (LOG.isDebugEnabled()) {
236           LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
237               "Generated D3" + " output as json = " + d3OutputJsonOutput);
238         }
239
240         if (d3OutputJsonOutput != null) {
241           returnValue.setResultCode(200);
242           returnValue.setResult(d3OutputJsonOutput);
243         } else {
244           returnValue.setResult(500, "Failed to generate D3 graph visualization");
245         }
246
247       } catch (Exception exc) {
248         returnValue.setResult(500,
249             "Failed to generate D3 graph visualization, due to a servlet exception.");
250         LOG.error(AaiUiMsgs.ERROR_D3_GRAPH_VISUALIZATION, exc.getLocalizedMessage());
251       }
252     } else {
253       returnValue.setResult(dataCollectionResult.getResultCode(), dataCollectionResult.getResult());
254     }
255
256     return returnValue;
257
258   }
259   
260   
261   /**
262    * Gets the visualization output basedon generic query.
263    *
264    * @param searchtargetEntity entity that will be used to start visualization flow
265    * @param queryParams the query params
266    * @return the visualization output basedon generic query
267    * @throws ServletException the servlet exception
268    * @throws  
269    */
270   private String getVisualizationOutputBasedonGenericQuery(SearchableEntity searchtargetEntity,
271       QueryParams queryParams, QueryRequest request) throws ServletException {
272
273     long opStartTimeInMs = System.currentTimeMillis();
274
275     VisualizationTransformer transformer = null;
276     try {
277       transformer = new VisualizationTransformer(visualizationConfigs, subConfig);
278     } catch (Exception exc) {
279       throw new ServletException(
280           "Failed to create VisualizationTransformer instance because of execption", exc);
281     }
282
283     VisualizationContext visContext = null;
284     long contextId = secureRandom.nextLong();
285     try {
286         if ( visualizationConfigs.isGizmoEnabled()) {
287               visContext = new BaseGizmoVisualizationContext(contextId, this.gizmoAdapter, aaiExecutorService,
288                       this.visualizationConfigs, oxmEntityLookup);
289         } else {
290               visContext = new BaseVisualizationContext(contextId, this.aaiAdapter, aaiExecutorService,
291                       this.visualizationConfigs, oxmEntityLookup);
292         }
293         
294       contextMap.putIfAbsent(contextId, visContext);
295     } catch (Exception e1) {
296       LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT,
297           "While building Visualization Context, " + e1.getLocalizedMessage());
298       throw new ServletException(e1);
299     }
300
301     String jsonResponse = null;
302
303     long startTimeInMs = System.currentTimeMillis();
304
305     visContext.processSelfLinks(searchtargetEntity, queryParams);
306     contextMap.remove(contextId);
307
308     logOptime("collectSelfLinkNodes()", startTimeInMs);
309
310     /*
311      * Flatten the graphs into a set of Graph and Link nodes. In this method I want the node graph
312      * resulting from the edge-tag-query to be represented first, and then we'll layer in
313      * relationship data.
314      */
315     long overlayDataStartTimeInMs = System.currentTimeMillis();
316
317     Map<String, ActiveInventoryNode> cachedNodeMap = visContext.getNodeCache();
318
319     if (LOG.isDebugEnabled()) {
320
321       StringBuilder sb = new StringBuilder(128);
322
323       sb.append("\nCached Node Map:\n");
324       for (String k : cachedNodeMap.keySet()) {
325         sb.append("\n----");
326         sb.append("\n").append(cachedNodeMap.get(k).dumpNodeTree(true));
327       }
328
329       LOG.debug(AaiUiMsgs.DEBUG_GENERIC, sb.toString());
330     }
331
332     transformer.buildFlatNodeArrayFromGraphCollection(cachedNodeMap);
333     transformer.buildLinksFromGraphCollection(cachedNodeMap);
334
335     /*
336      * - Apply configuration-driven styling
337      * - Build the final transformation response object
338      * - Use information we have to populate the GraphMeta object
339      */
340
341     transformer.addSearchTargetAttributesToRootNode();
342     
343     GraphMeta graphMeta = new GraphMeta();
344
345     D3VisualizationOutput output = null;
346     try {
347       output = transformer
348           .generateVisualizationOutput((System.currentTimeMillis() - opStartTimeInMs), graphMeta);
349     } catch (JsonProcessingException exc) {
350       throw new ServletException("Caught an exception while generation visualization output", exc);
351     } catch (IOException exc) {
352       LOG.error(AaiUiMsgs.FAILURE_TO_PROCESS_REQUEST, exc.getLocalizedMessage());
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     aaiExecutorService.shutdown();
380   }
381   
382 }