46a70c35265ea7ee9c7cbf5469c17ce366beb189
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / viewandinspect / services / VisualizationTransformer.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.File;
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.UUID;
34
35 import org.onap.aai.cl.api.Logger;
36 import org.onap.aai.cl.eelf.LoggerFactory;
37 import org.onap.aai.sparky.config.SparkyResourceLoader;
38 import org.onap.aai.sparky.logging.AaiUiMsgs;
39 import org.onap.aai.sparky.subscription.config.SubscriptionConfig;
40 import org.onap.aai.sparky.util.ConfigHelper;
41 import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs;
42 import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode;
43 import org.onap.aai.sparky.viewandinspect.entity.D3VisualizationOutput;
44 import org.onap.aai.sparky.viewandinspect.entity.GraphMeta;
45 import org.onap.aai.sparky.viewandinspect.entity.NodeDebug;
46 import org.onap.aai.sparky.viewandinspect.entity.SparkyGraphLink;
47 import org.onap.aai.sparky.viewandinspect.entity.SparkyGraphNode;
48
49 import com.fasterxml.jackson.core.JsonProcessingException;
50 import com.fasterxml.jackson.databind.ObjectMapper;
51 import com.fasterxml.jackson.databind.ObjectWriter;
52
53 /**
54  * The idea here is to receive a collection of graphs and then fold them together (or not) based on
55  * configuration. The first goal will be to fold all like-resources together, but the choice of
56  * folding could/should be configurable, and will simply change the degree of link based nodes when
57  * we generate the Node-Array and Link-Array output.
58  * 
59  * @author DAVEA
60  *
61  */
62
63 public class VisualizationTransformer {
64
65   private static final Logger LOG = LoggerFactory.getInstance().getLogger(
66       VisualizationTransformer.class);
67
68   List<SparkyGraphNode> flatNodeArray = new ArrayList<SparkyGraphNode>();
69
70   /*
71    * Maybe this isn't a string but Json-Model objects that we will convert to final string
72    * representation when we dump the node-array and link-array collections the post-data blob in the
73    * HttpServletResponse.
74    */
75
76   List<SparkyGraphLink> linkArrayOutput = new ArrayList<SparkyGraphLink>();
77   
78   private VisualizationConfigs visualizationConfigs;
79   private SubscriptionConfig subConfig;
80
81   /**
82    * Instantiates a new visualization transformer.
83    *
84    * @throws Exception the exception
85    */
86   public VisualizationTransformer(VisualizationConfigs visualizationConfigs,
87       SubscriptionConfig subConfig) throws Exception {
88           this.visualizationConfigs = visualizationConfigs; 
89           this.subConfig = subConfig;
90   }
91
92
93   /**
94    * Log optime.
95    *
96    * @param method the method
97    * @param startTimeInMs the start time in ms
98    */
99   private void logOptime(String method, long startTimeInMs) {
100     LOG.info(AaiUiMsgs.OPERATION_TIME, method,
101         String.valueOf((System.currentTimeMillis() - startTimeInMs)));
102   }
103
104   /**
105    * Adds the search target attributes to root node.
106    */
107   public void addSearchTargetAttributesToRootNode() {
108
109     for (SparkyGraphNode n : flatNodeArray) {
110       if (n.isRootNode()) {
111         n.getNodeMeta().setSearchTarget(true);
112         n.getNodeMeta().setClassName(this.visualizationConfigs.getSelectedSearchedNodeClassName());
113       }
114
115     }
116
117   }
118
119   /**
120    * Generate visualization output.
121    *
122    * @param preProcessingOpTimeInMs the pre processing op time in ms
123    * @param graphMeta the graph meta
124    * @return the d 3 visualization output
125    * @throws JsonProcessingException the json processing exception
126    * @throws IOException Signals that an I/O exception has occurred.
127    */
128
129   public D3VisualizationOutput generateVisualizationOutput(long preProcessingOpTimeInMs,
130       GraphMeta graphMeta) throws JsonProcessingException, IOException {
131
132     long opStartTimeInMs = System.currentTimeMillis();
133
134     /*
135      * iterate over the flat collection, and only add the graph nodes to the graph node collection
136      */
137
138     D3VisualizationOutput output = new D3VisualizationOutput();
139
140     output.setGraphMeta(graphMeta);
141
142     for (SparkyGraphNode n : flatNodeArray) {
143       if ( n.getItemType()!= null) {
144         output.pegCounter(n.getItemType());
145       }
146     }
147
148     output.addNodes(flatNodeArray);
149     output.addLinks(linkArrayOutput);
150
151     int numNodes = flatNodeArray.size();
152     int numLinks = linkArrayOutput.size();
153
154     LOG.info(AaiUiMsgs.VISUALIZATION_GRAPH_OUTPUT, String.valueOf(numNodes),
155         String.valueOf(numLinks));
156
157     if (numLinks < (numNodes - 1)) {
158       LOG.warn(AaiUiMsgs.DANGLING_NODE_WARNING, String.valueOf(numLinks),
159           String.valueOf(numNodes));
160     }
161
162     ObjectMapper mapper = new ObjectMapper();
163
164     SparkyResourceLoader resourceLoader = visualizationConfigs.getResourceLoader();
165     File aaiEntityDescriptorsFile = resourceLoader.getResourceAsFile(visualizationConfigs.getAaiEntityNodeDescriptors(), true);
166     
167     if (aaiEntityDescriptorsFile != null) {
168       com.fasterxml.jackson.databind.JsonNode aaiEntityNodeDefinitions =
169           mapper.readTree(aaiEntityDescriptorsFile);
170       graphMeta.setAaiEntityNodeDescriptors(aaiEntityNodeDefinitions);
171     } else {
172       LOG.error(AaiUiMsgs.ERROR_GENERIC, "Failed to find " + visualizationConfigs.getAaiEntityNodeDescriptors());
173       graphMeta.setAaiEntityNodeDescriptors(null);
174     }
175
176     graphMeta.setNumLinks(linkArrayOutput.size());
177     graphMeta.setNumNodes(flatNodeArray.size());
178     graphMeta.setRenderTimeInMs(preProcessingOpTimeInMs);
179
180     output.setGraphMeta(graphMeta);
181
182     logOptime("generateVisualizationOutput()", opStartTimeInMs);
183
184     return output;
185   }
186
187   /**
188    * Convert visualization output to json.
189    *
190    * @param output the output
191    * @return the string
192    * @throws JsonProcessingException the json processing exception
193    */
194   public String convertVisualizationOutputToJson(D3VisualizationOutput output)
195       throws JsonProcessingException {
196
197     if (output == null) {
198       return null;
199     }
200
201     ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
202
203     return ow.writeValueAsString(output);
204
205   }
206
207   /**
208    * Builds the links from graph collection.
209    *
210    * @param nodeMap the node map
211    */
212   public void buildLinksFromGraphCollection(Map<String, ActiveInventoryNode> nodeMap) {
213
214     for (ActiveInventoryNode ain : nodeMap.values()) {
215
216       /*
217        * This one is a little bit different, when we iterate over the collection we only want to
218        * draw the links for node that are less than the max traversal depth. We want to only draw
219        * links at a depth of n-1 because we are basing the links on the outbound neighbors from the
220        * current node.
221        */
222
223       if (ain.getNodeDepth() < this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) {
224
225         Collection<String> outboundNeighbors = ain.getOutboundNeighbors();
226
227         for (String outboundNeighbor : outboundNeighbors) {
228
229           SparkyGraphLink nodeLink = new SparkyGraphLink();
230
231           nodeLink.setId(UUID.randomUUID().toString());
232           nodeLink.setSource(ain.getNodeId());
233           nodeLink.setTarget(outboundNeighbor);
234
235           linkArrayOutput.add(nodeLink);
236
237         }
238
239         Collection<String> inboundNeighbors = ain.getInboundNeighbors();
240
241         for (String inboundNeighbor : inboundNeighbors) {
242
243           SparkyGraphLink nodeLink = new SparkyGraphLink();
244
245           nodeLink.setId(UUID.randomUUID().toString());
246           nodeLink.setSource(ain.getNodeId());
247           nodeLink.setTarget(inboundNeighbor);
248
249           linkArrayOutput.add(nodeLink);
250
251         }
252
253
254       } else {
255         if (LOG.isDebugEnabled()) {
256           LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "buildLinks(),"
257               + " Filtering node = " + ain.getNodeId() + " @ depth = "
258               + ain.getNodeDepth());
259         }
260
261       }
262     }
263
264   }
265
266   /**
267    * Builds the flat node array from graph collection.
268    *
269    * @param nodeMap the node map
270    */
271   /*
272    * Recursive function to walk multi-graph nodes and children to build a folded resource target
273    * graph.
274    */
275   public void buildFlatNodeArrayFromGraphCollection(Map<String, ActiveInventoryNode> nodeMap) {
276
277     for (ActiveInventoryNode n : nodeMap.values()) {
278
279       if (n.getNodeDepth() <= this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) {
280
281         SparkyGraphNode jsonNode = new SparkyGraphNode(n, this.visualizationConfigs, this.subConfig);
282
283         jsonNode.getNodeMeta().setClassName(this.visualizationConfigs.getGeneralNodeClassName());
284
285         if (this.visualizationConfigs.isVisualizationDebugEnabled()) {
286
287           NodeDebug nodeDebug = jsonNode.getNodeMeta().getNodeDebug();
288
289           if (nodeDebug != null) {
290             nodeDebug.setProcessingError(n.isProcessingErrorOccurred());
291             nodeDebug.setProcessingErrorCauses(n.getProcessingErrorCauses());
292           }
293         }
294         flatNodeArray.add(jsonNode);
295       } else {
296         if (LOG.isDebugEnabled()) {
297           LOG.debug(AaiUiMsgs.DEBUG_GENERIC, 
298               "Filtering node from visualization: " + n.getNodeId() + " @ depth = "
299               + n.getNodeDepth());
300         }
301       }
302     }
303   }
304
305 }