2 * ============LICENSE_START=======================================================
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23 package org.onap.aai.sparky.viewandinspect.services;
25 import java.io.IOException;
26 import java.security.SecureRandom;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ExecutorService;
31 import javax.servlet.ServletException;
33 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
34 import org.onap.aai.sparky.dal.aai.ActiveInventoryAdapter;
35 import org.onap.aai.sparky.dal.aai.ActiveInventoryDataProvider;
36 import org.onap.aai.sparky.dal.aai.config.ActiveInventoryConfig;
37 import org.onap.aai.sparky.dal.aai.config.ActiveInventoryRestConfig;
38 import org.onap.aai.sparky.dal.cache.EntityCache;
39 import org.onap.aai.sparky.dal.cache.PersistentEntityCache;
40 import org.onap.aai.sparky.dal.elasticsearch.ElasticSearchAdapter;
41 import org.onap.aai.sparky.dal.elasticsearch.ElasticSearchDataProvider;
42 import org.onap.aai.sparky.dal.elasticsearch.config.ElasticSearchConfig;
43 import org.onap.aai.sparky.dal.rest.OperationResult;
44 import org.onap.aai.sparky.dal.rest.RestClientBuilder;
45 import org.onap.aai.sparky.dal.rest.RestfulDataAccessor;
46 import org.onap.aai.sparky.logging.AaiUiMsgs;
47 import org.onap.aai.sparky.synchronizer.entity.SearchableEntity;
48 import org.onap.aai.sparky.util.NodeUtils;
49 import org.onap.aai.sparky.viewandinspect.config.VisualizationConfig;
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 import org.onap.aai.cl.api.Logger;
56 import org.onap.aai.cl.eelf.LoggerFactory;
58 import com.fasterxml.jackson.annotation.JsonInclude.Include;
59 import com.fasterxml.jackson.core.JsonProcessingException;
60 import com.fasterxml.jackson.databind.DeserializationFeature;
61 import com.fasterxml.jackson.databind.JsonNode;
62 import com.fasterxml.jackson.databind.ObjectMapper;
64 public class VisualizationService {
66 private static final Logger LOG =
67 LoggerFactory.getInstance().getLogger(VisualizationService.class);
69 private OxmModelLoader loader;
70 private ObjectMapper mapper = new ObjectMapper();
72 private final ActiveInventoryDataProvider aaiProvider;
73 private final ActiveInventoryRestConfig aaiRestConfig;
74 private final ElasticSearchDataProvider esProvider;
75 private final ElasticSearchConfig esConfig;
76 private final ExecutorService aaiExecutorService;
78 private ConcurrentHashMap<Long, VisualizationContext> contextMap;
79 private final SecureRandom secureRandom;
81 private ActiveInventoryConfig aaiConfig;
82 private VisualizationConfig visualizationConfig;
84 public VisualizationService(OxmModelLoader loader) throws Exception {
87 aaiRestConfig = ActiveInventoryConfig.getConfig().getAaiRestConfig();
89 EntityCache cache = null;
90 secureRandom = new SecureRandom();
92 ActiveInventoryAdapter aaiAdapter = new ActiveInventoryAdapter(new RestClientBuilder());
94 if (aaiRestConfig.isCacheEnabled()) {
95 cache = new PersistentEntityCache(aaiRestConfig.getStorageFolderOverride(),
96 aaiRestConfig.getNumCacheWorkers());
98 aaiAdapter.setCacheEnabled(true);
99 aaiAdapter.setEntityCache(cache);
102 this.aaiProvider = aaiAdapter;
104 RestClientBuilder esClientBuilder = new RestClientBuilder();
105 esClientBuilder.setUseHttps(false);
106 RestfulDataAccessor nonCachingRestProvider = new RestfulDataAccessor(esClientBuilder);
107 this.esConfig = ElasticSearchConfig.getConfig();
108 this.esProvider = new ElasticSearchAdapter(nonCachingRestProvider, this.esConfig);
110 this.mapper = new ObjectMapper();
111 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
113 this.contextMap = new ConcurrentHashMap<Long, VisualizationContext>();
114 this.visualizationConfig = VisualizationConfig.getConfig();
115 this.aaiConfig = ActiveInventoryConfig.getConfig();
116 this.aaiExecutorService = NodeUtils.createNamedExecutor("SLNC-WORKER",
117 aaiConfig.getAaiRestConfig().getNumResolverWorkers(), LOG);
120 public OxmModelLoader getLoader() {
124 public void setLoader(OxmModelLoader loader) {
125 this.loader = loader;
129 * Analyze query request body.
131 * @param queryRequestJson the query request json
132 * @return the query request
135 public QueryRequest analyzeQueryRequestBody(String queryRequestJson) {
138 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
139 "analyzeQueryRequestBody()," + " queryRequestJson = " + queryRequestJson);
141 ObjectMapper nonEmptyMapper = new ObjectMapper();
142 nonEmptyMapper.setSerializationInclusion(Include.NON_EMPTY);
144 QueryRequest queryBody = null;
147 queryBody = nonEmptyMapper.readValue(queryRequestJson, QueryRequest.class);
148 } catch (Exception exc) {
149 LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, "Analyzing query request body.",
150 exc.getLocalizedMessage());
160 * @param method the method
161 * @param opStartTimeInMs the op start time in ms
163 private void logOptime(String method, long opStartTimeInMs) {
164 LOG.info(AaiUiMsgs.OPERATION_TIME, method,
165 String.valueOf(System.currentTimeMillis() - opStartTimeInMs));
168 private SearchableEntity extractSearchableEntityFromElasticEntity(OperationResult operationResult) {
169 if (operationResult == null || !operationResult.wasSuccessful()) {
170 // error, return empty collection
174 SearchableEntity sourceEntity = null;
175 if (operationResult.wasSuccessful()) {
178 JsonNode elasticValue = mapper.readValue(operationResult.getResult(), JsonNode.class);
180 if (elasticValue != null) {
181 JsonNode sourceField = elasticValue.get("_source");
183 if (sourceField != null) {
184 sourceEntity = new SearchableEntity();
186 String entityType = NodeUtils.extractFieldValueFromObject(sourceField, "entityType");
187 sourceEntity.setEntityType(entityType);
188 String entityPrimaryKeyValue = NodeUtils.extractFieldValueFromObject(sourceField, "entityPrimaryKeyValue");
189 sourceEntity.setEntityPrimaryKeyValue(entityPrimaryKeyValue);
190 String link = NodeUtils.extractFieldValueFromObject(sourceField, "link");
191 sourceEntity.setLink(link);
192 String lastmodTimestamp = NodeUtils.extractFieldValueFromObject(sourceField, "lastmodTimestamp");
193 sourceEntity.setEntityTimeStamp(lastmodTimestamp);
196 } catch (IOException ioe) {
197 LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "a json node ", ioe.getLocalizedMessage());
204 * Builds the visualization using generic query.
206 * @param queryRequest the query request
207 * @return the operation result
209 public OperationResult buildVisualizationUsingGenericQuery(QueryRequest queryRequest) {
211 OperationResult returnValue = new OperationResult();
212 OperationResult dataCollectionResult = null;
213 QueryParams queryParams = null;
214 SearchableEntity sourceEntity = null;
219 * Here is where we need to make a dip to elastic-search for the self-link by entity-id (link
222 dataCollectionResult = esProvider.retrieveEntityById(queryRequest.getHashId());
223 sourceEntity = extractSearchableEntityFromElasticEntity(dataCollectionResult);
225 if (sourceEntity != null) {
226 sourceEntity.generateId();
229 queryParams = new QueryParams();
230 queryParams.setSearchTargetNodeId(queryRequest.getHashId());
232 } catch (Exception e1) {
233 LOG.error(AaiUiMsgs.FAILED_TO_GET_NODES_QUERY_RESULT, e1.getLocalizedMessage());
234 dataCollectionResult = new OperationResult(500, "Failed to get nodes-query result from AAI");
237 if (dataCollectionResult.getResultCode() == 200) {
239 String d3OutputJsonOutput = null;
243 d3OutputJsonOutput = getVisualizationOutputBasedonGenericQuery(sourceEntity, queryParams);
245 if (LOG.isDebugEnabled()) {
246 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
247 "Generated D3" + " output as json = " + d3OutputJsonOutput);
250 if (d3OutputJsonOutput != null) {
251 returnValue.setResultCode(200);
252 returnValue.setResult(d3OutputJsonOutput);
254 returnValue.setResult(500, "Failed to generate D3 graph visualization");
257 } catch (Exception exc) {
258 returnValue.setResult(500,
259 "Failed to generate D3 graph visualization, due to a servlet exception.");
260 LOG.error(AaiUiMsgs.ERROR_D3_GRAPH_VISUALIZATION, exc.getLocalizedMessage());
263 returnValue.setResult(dataCollectionResult.getResultCode(), dataCollectionResult.getResult());
271 * Gets the visualization output basedon generic query.
273 * @param searchtargetEntity entity that will be used to start visualization flow
274 * @param queryParams the query params
275 * @return the visualization output basedon generic query
276 * @throws ServletException the servlet exception
278 private String getVisualizationOutputBasedonGenericQuery(SearchableEntity searchtargetEntity,
279 QueryParams queryParams) throws ServletException {
281 long opStartTimeInMs = System.currentTimeMillis();
283 VisualizationTransformer transformer = null;
285 transformer = new VisualizationTransformer();
286 } catch (Exception exc) {
287 throw new ServletException(
288 "Failed to create VisualizationTransformer instance because of execption", exc);
291 VisualizationContext visContext = null;
292 long contextId = secureRandom.nextLong();
294 visContext = new VisualizationContext(contextId, aaiProvider, aaiExecutorService, loader);
295 contextMap.putIfAbsent(contextId, visContext);
296 } catch (Exception e1) {
297 LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT,
298 "While building Visualization Context, " + e1.getLocalizedMessage());
299 throw new ServletException(e1);
302 String jsonResponse = null;
304 long startTimeInMs = System.currentTimeMillis();
306 visContext.processSelfLinks(searchtargetEntity, queryParams);
307 contextMap.remove(contextId);
309 logOptime("collectSelfLinkNodes()", startTimeInMs);
312 * Flatten the graphs into a set of Graph and Link nodes. In this method I want the node graph
313 * resulting from the edge-tag-query to be represented first, and then we'll layer in
316 long overlayDataStartTimeInMs = System.currentTimeMillis();
318 Map<String, ActiveInventoryNode> cachedNodeMap = visContext.getNodeCache();
320 if (LOG.isDebugEnabled()) {
322 StringBuilder sb = new StringBuilder(128);
324 sb.append("\nCached Node Map:\n");
325 for (String k : cachedNodeMap.keySet()) {
327 sb.append("\n").append(cachedNodeMap.get(k).dumpNodeTree(true));
330 LOG.debug(AaiUiMsgs.DEBUG_GENERIC, sb.toString());
333 transformer.buildFlatNodeArrayFromGraphCollection(cachedNodeMap);
334 transformer.buildLinksFromGraphCollection(cachedNodeMap);
337 * - Apply configuration-driven styling
338 * - Build the final transformation response object
339 * - Use information we have to populate the GraphMeta object
342 transformer.addSearchTargetAttributesToRootNode();
344 GraphMeta graphMeta = new GraphMeta();
346 D3VisualizationOutput output = null;
349 .generateVisualizationOutput((System.currentTimeMillis() - opStartTimeInMs), graphMeta);
350 } catch (Exception exc) {
351 LOG.error(AaiUiMsgs.FAILURE_TO_PROCESS_REQUEST, exc.getLocalizedMessage());
352 throw new ServletException("Caught an exception while generation visualization output", exc);
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());
363 jsonResponse = transformer.convertVisualizationOutputToJson(output);
364 } catch (JsonProcessingException jpe) {
365 throw new ServletException(
366 "Caught an exception while converting visualization output to json", jpe);
369 logOptime("[build flat node array, add relationship data, search target,"
370 + " color scheme, and generate visualization output]", overlayDataStartTimeInMs);
372 logOptime("doFilter()", opStartTimeInMs);
378 public void shutdown() {
379 aaiProvider.shutdown();
380 aaiExecutorService.shutdown();
381 esProvider.shutdown();