2 * ============LICENSE_START===================================================
3 * SPARKY (AAI UI service)
4 * ============================================================================
5 * Copyright © 2017 AT&T Intellectual Property.
6 * Copyright © 2017 Amdocs
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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=====================================================
22 * ECOMP and OpenECOMP are trademarks
23 * and service marks of AT&T Intellectual Property.
26 package org.openecomp.sparky.viewandinspect.services;
28 import java.io.IOException;
29 import java.security.SecureRandom;
31 import java.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.ExecutorService;
34 import javax.servlet.ServletException;
36 import org.openecomp.cl.api.Logger;
37 import org.openecomp.cl.eelf.LoggerFactory;
38 import org.openecomp.sparky.config.oxm.OxmModelLoader;
39 import org.openecomp.sparky.dal.aai.ActiveInventoryAdapter;
40 import org.openecomp.sparky.dal.aai.ActiveInventoryDataProvider;
41 import org.openecomp.sparky.dal.aai.config.ActiveInventoryConfig;
42 import org.openecomp.sparky.dal.aai.config.ActiveInventoryRestConfig;
43 import org.openecomp.sparky.dal.cache.EntityCache;
44 import org.openecomp.sparky.dal.cache.PersistentEntityCache;
45 import org.openecomp.sparky.dal.elasticsearch.ElasticSearchAdapter;
46 import org.openecomp.sparky.dal.elasticsearch.ElasticSearchDataProvider;
47 import org.openecomp.sparky.dal.elasticsearch.config.ElasticSearchConfig;
48 import org.openecomp.sparky.dal.rest.OperationResult;
49 import org.openecomp.sparky.dal.rest.RestClientBuilder;
50 import org.openecomp.sparky.dal.rest.RestfulDataAccessor;
51 import org.openecomp.sparky.logging.AaiUiMsgs;
52 import org.openecomp.sparky.synchronizer.entity.SearchableEntity;
53 import org.openecomp.sparky.util.NodeUtils;
54 import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
55 import org.openecomp.sparky.viewandinspect.entity.ActiveInventoryNode;
56 import org.openecomp.sparky.viewandinspect.entity.D3VisualizationOutput;
57 import org.openecomp.sparky.viewandinspect.entity.GraphMeta;
58 import org.openecomp.sparky.viewandinspect.entity.QueryParams;
59 import org.openecomp.sparky.viewandinspect.entity.QueryRequest;
61 import com.fasterxml.jackson.annotation.JsonInclude.Include;
62 import com.fasterxml.jackson.core.JsonProcessingException;
63 import com.fasterxml.jackson.databind.DeserializationFeature;
64 import com.fasterxml.jackson.databind.JsonNode;
65 import com.fasterxml.jackson.databind.ObjectMapper;
67 public class VisualizationService {
69 private static final Logger LOG =
70 LoggerFactory.getInstance().getLogger(VisualizationService.class);
72 private OxmModelLoader loader;
73 private ObjectMapper mapper = new ObjectMapper();
75 private final ActiveInventoryDataProvider aaiProvider;
76 private final ActiveInventoryRestConfig aaiRestConfig;
77 private final ElasticSearchDataProvider esProvider;
78 private final ElasticSearchConfig esConfig;
79 private final ExecutorService aaiExecutorService;
81 private ConcurrentHashMap<Long, VisualizationContext> contextMap;
82 private final SecureRandom secureRandom;
84 private ActiveInventoryConfig aaiConfig;
85 private VisualizationConfig visualizationConfig;
87 public VisualizationService(OxmModelLoader loader) throws Exception {
90 aaiRestConfig = ActiveInventoryConfig.getConfig().getAaiRestConfig();
92 EntityCache cache = null;
93 secureRandom = new SecureRandom();
95 ActiveInventoryAdapter aaiAdapter = new ActiveInventoryAdapter(new RestClientBuilder());
97 if (aaiRestConfig.isCacheEnabled()) {
98 cache = new PersistentEntityCache(aaiRestConfig.getStorageFolderOverride(),
99 aaiRestConfig.getNumCacheWorkers());
101 aaiAdapter.setCacheEnabled(true);
102 aaiAdapter.setEntityCache(cache);
105 this.aaiProvider = aaiAdapter;
107 RestClientBuilder esClientBuilder = new RestClientBuilder();
108 esClientBuilder.setUseHttps(false);
109 RestfulDataAccessor nonCachingRestProvider = new RestfulDataAccessor(esClientBuilder);
110 this.esConfig = ElasticSearchConfig.getConfig();
111 this.esProvider = new ElasticSearchAdapter(nonCachingRestProvider, this.esConfig);
113 this.mapper = new ObjectMapper();
114 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
116 this.contextMap = new ConcurrentHashMap<Long, VisualizationContext>();
117 this.visualizationConfig = VisualizationConfig.getConfig();
118 this.aaiConfig = ActiveInventoryConfig.getConfig();
119 this.aaiExecutorService = NodeUtils.createNamedExecutor("SLNC-WORKER",
120 aaiConfig.getAaiRestConfig().getNumResolverWorkers(), LOG);
123 public OxmModelLoader getLoader() {
127 public void setLoader(OxmModelLoader loader) {
128 this.loader = loader;
132 * Analyze query request body.
134 * @param queryRequestJson the query request json
135 * @return the query request
138 public QueryRequest analyzeQueryRequestBody(String queryRequestJson) {
141 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
142 "analyzeQueryRequestBody()," + " queryRequestJson = " + queryRequestJson);
144 ObjectMapper nonEmptyMapper = new ObjectMapper();
145 nonEmptyMapper.setSerializationInclusion(Include.NON_EMPTY);
147 QueryRequest queryBody = null;
150 queryBody = nonEmptyMapper.readValue(queryRequestJson, QueryRequest.class);
151 } catch (Exception exc) {
152 LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, "Analyzing query request body.",
153 exc.getLocalizedMessage());
163 * @param method the method
164 * @param opStartTimeInMs the op start time in ms
166 private void logOptime(String method, long opStartTimeInMs) {
167 LOG.info(AaiUiMsgs.OPERATION_TIME, method,
168 String.valueOf(System.currentTimeMillis() - opStartTimeInMs));
171 private SearchableEntity extractSearchableEntityFromElasticEntity(OperationResult operationResult) {
172 if (operationResult == null || !operationResult.wasSuccessful()) {
173 // error, return empty collection
177 SearchableEntity sourceEntity = null;
178 if (operationResult.wasSuccessful()) {
181 JsonNode elasticValue = mapper.readValue(operationResult.getResult(), JsonNode.class);
183 if (elasticValue != null) {
184 JsonNode sourceField = elasticValue.get("_source");
186 if (sourceField != null) {
187 sourceEntity = new SearchableEntity();
189 String entityType = NodeUtils.extractFieldValueFromObject(sourceField, "entityType");
190 sourceEntity.setEntityType(entityType);
191 String entityPrimaryKeyValue = NodeUtils.extractFieldValueFromObject(sourceField, "entityPrimaryKeyValue");
192 sourceEntity.setEntityPrimaryKeyValue(entityPrimaryKeyValue);
193 String link = NodeUtils.extractFieldValueFromObject(sourceField, "link");
194 sourceEntity.setLink(link);
195 String lastmodTimestamp = NodeUtils.extractFieldValueFromObject(sourceField, "lastmodTimestamp");
196 sourceEntity.setEntityTimeStamp(lastmodTimestamp);
199 } catch (IOException ioe) {
200 LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "a json node ", ioe.getLocalizedMessage());
207 * Builds the visualization using generic query.
209 * @param queryRequest the query request
210 * @return the operation result
212 public OperationResult buildVisualizationUsingGenericQuery(QueryRequest queryRequest) {
214 OperationResult returnValue = new OperationResult();
215 OperationResult dataCollectionResult = null;
216 QueryParams queryParams = null;
217 SearchableEntity sourceEntity = null;
222 * Here is where we need to make a dip to elastic-search for the self-link by entity-id (link
225 dataCollectionResult = esProvider.retrieveEntityById(queryRequest.getHashId());
226 sourceEntity = extractSearchableEntityFromElasticEntity(dataCollectionResult);
228 if (sourceEntity != null) {
229 sourceEntity.generateId();
232 queryParams = new QueryParams();
233 queryParams.setSearchTargetNodeId(queryRequest.getHashId());
235 } catch (Exception e1) {
236 LOG.error(AaiUiMsgs.FAILED_TO_GET_NODES_QUERY_RESULT, e1.getLocalizedMessage());
237 dataCollectionResult = new OperationResult(500, "Failed to get nodes-query result from AAI");
240 if (dataCollectionResult.getResultCode() == 200) {
242 String d3OutputJsonOutput = null;
246 d3OutputJsonOutput = getVisualizationOutputBasedonGenericQuery(sourceEntity, queryParams);
248 if (LOG.isDebugEnabled()) {
249 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
250 "Generated D3" + " output as json = " + d3OutputJsonOutput);
253 if (d3OutputJsonOutput != null) {
254 returnValue.setResultCode(200);
255 returnValue.setResult(d3OutputJsonOutput);
257 returnValue.setResult(500, "Failed to generate D3 graph visualization");
260 } catch (Exception exc) {
261 returnValue.setResult(500,
262 "Failed to generate D3 graph visualization, due to a servlet exception.");
263 LOG.error(AaiUiMsgs.ERROR_D3_GRAPH_VISUALIZATION, exc.getLocalizedMessage());
266 returnValue.setResult(dataCollectionResult.getResultCode(), dataCollectionResult.getResult());
274 * Gets the visualization output basedon generic query.
276 * @param searchtargetEntity entity that will be used to start visualization flow
277 * @param queryParams the query params
278 * @return the visualization output basedon generic query
279 * @throws ServletException the servlet exception
281 private String getVisualizationOutputBasedonGenericQuery(SearchableEntity searchtargetEntity,
282 QueryParams queryParams) throws ServletException {
284 long opStartTimeInMs = System.currentTimeMillis();
286 VisualizationTransformer transformer = null;
288 transformer = new VisualizationTransformer();
289 } catch (Exception exc) {
290 throw new ServletException(
291 "Failed to create VisualizationTransformer instance because of execption", exc);
294 VisualizationContext visContext = null;
295 long contextId = secureRandom.nextLong();
297 visContext = new VisualizationContext(contextId, aaiProvider, aaiExecutorService, loader);
298 contextMap.putIfAbsent(contextId, visContext);
299 } catch (Exception e1) {
300 LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT,
301 "While building Visualization Context, " + e1.getLocalizedMessage());
302 throw new ServletException(e1);
305 String jsonResponse = null;
307 long startTimeInMs = System.currentTimeMillis();
309 visContext.processSelfLinks(searchtargetEntity, queryParams);
310 contextMap.remove(contextId);
312 logOptime("collectSelfLinkNodes()", startTimeInMs);
315 * Flatten the graphs into a set of Graph and Link nodes. In this method I want the node graph
316 * resulting from the edge-tag-query to be represented first, and then we'll layer in
319 long overlayDataStartTimeInMs = System.currentTimeMillis();
321 Map<String, ActiveInventoryNode> cachedNodeMap = visContext.getNodeCache();
323 if (LOG.isDebugEnabled()) {
325 StringBuilder sb = new StringBuilder(128);
327 sb.append("\nCached Node Map:\n");
328 for (String k : cachedNodeMap.keySet()) {
330 sb.append("\n").append(cachedNodeMap.get(k).dumpNodeTree(true));
333 LOG.debug(AaiUiMsgs.DEBUG_GENERIC, sb.toString());
336 transformer.buildFlatNodeArrayFromGraphCollection(cachedNodeMap);
337 transformer.buildLinksFromGraphCollection(cachedNodeMap);
340 * - Apply configuration-driven styling
341 * - Build the final transformation response object
342 * - Use information we have to populate the GraphMeta object
345 transformer.addSearchTargetAttributesToRootNode();
347 GraphMeta graphMeta = new GraphMeta();
349 D3VisualizationOutput output = null;
352 .generateVisualizationOutput((System.currentTimeMillis() - opStartTimeInMs), graphMeta);
353 } catch (JsonProcessingException exc) {
354 throw new ServletException("Caught an exception while generation visualization output", exc);
355 } catch (IOException exc) {
356 LOG.error(AaiUiMsgs.FAILURE_TO_PROCESS_REQUEST, exc.getLocalizedMessage());
359 output.setInlineMessage(visContext.getInlineMessage());
360 output.getGraphMeta().setNumLinkResolveFailed(visContext.getNumFailedLinkResolve());
361 output.getGraphMeta().setNumLinksResolvedSuccessfullyFromCache(
362 visContext.getNumSuccessfulLinkResolveFromCache());
363 output.getGraphMeta().setNumLinksResolvedSuccessfullyFromServer(
364 visContext.getNumSuccessfulLinkResolveFromFromServer());
367 jsonResponse = transformer.convertVisualizationOutputToJson(output);
368 } catch (JsonProcessingException jpe) {
369 throw new ServletException(
370 "Caught an exception while converting visualization output to json", jpe);
373 logOptime("[build flat node array, add relationship data, search target,"
374 + " color scheme, and generate visualization output]", overlayDataStartTimeInMs);
376 logOptime("doFilter()", opStartTimeInMs);
382 public void shutdown() {
383 aaiProvider.shutdown();
384 aaiExecutorService.shutdown();
385 esProvider.shutdown();