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.openecomp.sparky.viewandinspect.entity;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
32 import java.util.Map.Entry;
34 import java.util.concurrent.ConcurrentLinkedDeque;
35 import java.util.concurrent.atomic.AtomicBoolean;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
39 import org.openecomp.cl.api.Logger;
40 import org.openecomp.cl.eelf.LoggerFactory;
41 import org.openecomp.sparky.config.oxm.OxmModelLoader;
42 import org.openecomp.sparky.dal.rest.OperationResult;
43 import org.openecomp.sparky.logging.AaiUiMsgs;
44 import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
45 import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingAction;
46 import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingState;
48 import com.fasterxml.jackson.databind.JsonNode;
49 import com.fasterxml.jackson.databind.ObjectMapper;
52 * The Class ActiveInventoryNode.
54 public class ActiveInventoryNode {
56 private static final Logger LOG = LoggerFactory.getInstance().getLogger(
57 ActiveInventoryNode.class);
58 private static final String URIRegexPattern = "aai/v[\\d]/";
60 public static final int DEFAULT_INIT_NODE_DEPTH = 1000;
62 private String nodeId;
63 private String selfLink;
65 private boolean isRootNode;
66 private ConcurrentLinkedDeque<String> inboundNeighbors;
67 private ConcurrentLinkedDeque<String> outboundNeighbors;
68 private List<JsonNode> complexGroups;
69 private List<RelationshipList> relationshipLists;
70 private int nodeDepth;
71 private OperationResult opResult;
74 private boolean processingErrorOccurred;
75 private List<String> errorCauses;
76 private boolean selflinkRetrievalFailure;
77 private NodeProcessingState state;
79 private boolean processedNeighbors;
81 private boolean selfLinkPendingResolve;
84 * I think we shouldn't be using this crutch flags. If these things are meant
85 * to represent the current state of the node, then they should be legitimate
89 private boolean selfLinkDeterminationPending;
91 private AtomicBoolean selfLinkProcessed;
93 private OxmModelLoader oxmModelLoader;
94 private VisualizationConfig visualizationConfig;
96 private String entityType;
97 private String primaryKeyName;
98 private String primaryKeyValue;
100 private boolean nodeIssue;
101 private boolean ignoredByFilter;
103 private boolean resolvedSelfLink;
105 private Map<String, String> properties;
106 private ArrayList<String> queryParams;
108 private ObjectMapper mapper;
111 * Instantiates a new active inventory node.
113 public ActiveInventoryNode() {
118 * Instantiates a new active inventory node.
122 public ActiveInventoryNode(String key) {
124 this.entityType = null;
125 this.selfLink = null;
126 this.properties = new HashMap<String, String>();
127 this.processingErrorOccurred = false;
128 this.errorCauses = new ArrayList<String>();
129 this.selflinkRetrievalFailure = false;
130 this.nodeIssue = false;
131 this.state = NodeProcessingState.INIT;
132 this.selfLinkPendingResolve = false;
133 this.selfLinkDeterminationPending = false;
135 selfLinkProcessed = new AtomicBoolean(Boolean.FALSE);
136 oxmModelLoader = null;
137 visualizationConfig = null;
140 inboundNeighbors = new ConcurrentLinkedDeque<String>();
141 outboundNeighbors = new ConcurrentLinkedDeque<String>();
142 complexGroups = new ArrayList<JsonNode>();
143 relationshipLists = new ArrayList<RelationshipList>();
144 nodeDepth = DEFAULT_INIT_NODE_DEPTH;
145 queryParams = new ArrayList<String>();
147 mapper = new ObjectMapper();
149 processedNeighbors = false;
150 resolvedSelfLink = false;
155 public void clearQueryParams() {
159 public void addQueryParam(String queryParam) {
160 if ( queryParam!= null) {
161 if( !queryParams.contains(queryParam)) {
162 queryParams.add(queryParam);
167 public void addQueryParams(Collection<String> params) {
169 if (params != null & params.size() > 0) {
171 for (String param : params) {
172 addQueryParam(param);
178 public List<String> getQueryParams() {
182 public void setSelfLinkDeterminationPending(boolean selfLinkDeterminationPending) {
183 this.selfLinkDeterminationPending = selfLinkDeterminationPending;
186 public boolean isSelfLinkDeterminationPending() {
187 return selfLinkDeterminationPending;
190 public NodeProcessingState getState() {
194 public List<JsonNode> getComplexGroups() {
195 return complexGroups;
198 public List<RelationshipList> getRelationshipLists() {
199 return relationshipLists;
202 public OperationResult getOpResult() {
206 public void setOpResult(OperationResult opResult) {
207 this.opResult = opResult;
210 public String getPrimaryKeyName() {
211 return primaryKeyName;
215 * Gets the visualization config.
217 * @return the visualization config
219 public VisualizationConfig getvisualizationConfig() {
220 return visualizationConfig;
223 public int getNodeDepth() {
227 public void setNodeDepth(int nodeDepth) {
228 this.nodeDepth = nodeDepth;
232 * Sets the visualization config.
234 * @param visualizationConfig the new visualization config
236 public void setvisualizationConfig(VisualizationConfig visualizationConfig) {
237 this.visualizationConfig = visualizationConfig;
240 public OxmModelLoader getOxmModelLoader() {
241 return oxmModelLoader;
244 public void setPrimaryKeyName(String primaryKeyName) {
245 this.primaryKeyName = primaryKeyName;
248 public String getPrimaryKeyValue() {
249 return primaryKeyValue;
252 public void setPrimaryKeyValue(String primaryKeyValue) {
253 this.primaryKeyValue = primaryKeyValue;
256 public boolean isNodeIssue() {
260 public boolean isIgnoredByFilter() {
261 return ignoredByFilter;
264 public void setIgnoredByFilter(boolean ignoredByFilter) {
265 this.ignoredByFilter = ignoredByFilter;
268 public void setNodeIssue(boolean nodeIssue) {
269 this.nodeIssue = nodeIssue;
273 * Checks for processed neighbors.
275 * @return true, if successful
277 public boolean hasProcessedNeighbors() {
278 return processedNeighbors;
281 public void setProcessedNeighbors(boolean processedNeighbors) {
282 this.processedNeighbors = processedNeighbors;
286 * Checks for resolved self link.
288 * @return true, if successful
290 public boolean hasResolvedSelfLink() {
291 return resolvedSelfLink;
294 public void setResolvedSelfLink(boolean resolvedSelfLink) {
295 this.resolvedSelfLink = resolvedSelfLink;
299 * Checks for neighbors.
301 * @return true, if successful
303 public boolean hasNeighbors() {
304 return (inboundNeighbors.size() > 0 || outboundNeighbors.size() > 0);
308 * Adds the inbound neighbor.
310 * @param nodeId the node id
312 public void addInboundNeighbor(String nodeId) {
314 if (nodeId == null) {
318 if (!inboundNeighbors.contains(nodeId)) {
319 inboundNeighbors.add(nodeId);
325 * Adds the outbound neighbor.
327 * @param nodeId the node id
329 public void addOutboundNeighbor(String nodeId) {
331 if (nodeId == null) {
335 if (!outboundNeighbors.contains(nodeId)) {
336 outboundNeighbors.add(nodeId);
341 public boolean isAtMaxDepth() {
342 return (nodeDepth >= VisualizationConfig.getConfig().getMaxSelfLinkTraversalDepth());
345 public ConcurrentLinkedDeque<String> getInboundNeighbors() {
346 return inboundNeighbors;
349 public void setInboundNeighbors(ConcurrentLinkedDeque<String> inboundNeighbors) {
350 this.inboundNeighbors = inboundNeighbors;
353 public Collection<String> getOutboundNeighbors() {
354 List<String> result = new ArrayList<String>();
356 Iterator<String> neighborIterator = outboundNeighbors.iterator();
358 while (neighborIterator.hasNext()) {
359 result.add(neighborIterator.next());
368 * @param newDepth the new depth
369 * @return true, if successful
371 public boolean changeDepth(int newDepth) {
373 boolean nodeDepthWasChanged = false;
375 if (newDepth < nodeDepth) {
376 LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_DEPTH, nodeId,
377 String.valueOf(this.nodeDepth), String.valueOf(newDepth));
378 this.nodeDepth = newDepth;
379 nodeDepthWasChanged = true;
382 return nodeDepthWasChanged;
386 public void setOutboundNeighbors(ConcurrentLinkedDeque<String> outboundNeighbors) {
387 this.outboundNeighbors = outboundNeighbors;
390 public boolean isRootNode() {
394 public void setRootNode(boolean isRootNode) {
395 this.isRootNode = isRootNode;
401 * @param newState the new state
402 * @param action the action
404 public void changeState(NodeProcessingState newState, NodeProcessingAction action) {
406 * NodeId may be null depending on the current node life-cycle state
409 if (getNodeId() != null) {
410 LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE, state.toString(), newState.toString(), action.toString());
412 LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE_NO_NODE_ID, state.toString(), newState.toString(), action.toString());
414 this.state = newState;
417 public boolean isSelfLinkPendingResolve() {
418 return selfLinkPendingResolve;
421 public void setSelfLinkPendingResolve(boolean selfLinkPendingResolve) {
422 this.selfLinkPendingResolve = selfLinkPendingResolve;
425 public boolean isSelflinkRetrievalFailure() {
426 return selflinkRetrievalFailure;
429 public void setSelflinkRetrievalFailure(boolean selflinkRetrievalFailure) {
430 this.selflinkRetrievalFailure = selflinkRetrievalFailure;
433 public void setOxmModelLoader(OxmModelLoader loader) {
434 this.oxmModelLoader = loader;
437 public boolean getSelfLinkProcessed() {
438 return selfLinkProcessed.get();
441 public void setSelfLinkProcessed(boolean selfLinkProcessed) {
442 this.selfLinkProcessed.set(selfLinkProcessed);
445 public boolean isDirectSelfLink() {
446 // https://<AAI-Hostname>:8443/aai/v8/resources/id/2458124400
447 return isDirectSelfLink(this.selfLink);
451 * Checks if is direct self link.
453 * @param link the link
454 * @return true, if is direct self link
456 public static boolean isDirectSelfLink(String link) {
457 // https://<AAI-Hostname>:8443/aai/v8/resources/id/2458124400
463 return link.contains("/resources/id/");
467 public Map<String, String> getProperties() {
472 * Adds the error cause.
474 * @param error the error
476 public void addErrorCause(String error) {
477 if (!errorCauses.contains(error)) {
478 errorCauses.add(error);
486 * @param value the value
488 public void addProperty(String key, String value) {
489 properties.put(key, value);
492 public boolean isProcessingErrorOccurred() {
493 return processingErrorOccurred;
496 public void setProcessingErrorOccurred(boolean processingErrorOccurred) {
497 this.processingErrorOccurred = processingErrorOccurred;
500 public String getNodeId() {
504 public void setNodeId(String nodeId) {
505 this.nodeId = nodeId;
508 public String getEntityType() {
512 public void setEntityType(String entityType) {
513 this.entityType = entityType;
516 public String getSelfLink() {
521 * Calculate edit attribute uri.
523 * @param link the link
526 public String calculateEditAttributeUri(String link) {
528 Pattern pattern = Pattern.compile(URIRegexPattern);
529 Matcher matcher = pattern.matcher(link);
530 if (matcher.find()) {
531 uri = link.substring(matcher.end());
537 * Analyze self link relationship list.
539 * @param jsonResult the json result
540 * @return the relationship list
542 private RelationshipList analyzeSelfLinkRelationshipList(String jsonResult) {
545 RelationshipList relationshipList = null;
548 relationshipList = mapper.readValue(jsonResult, RelationshipList.class);
549 } catch (Exception exc) {
550 LOG.error(AaiUiMsgs.SELF_LINK_RELATIONSHIP_LIST_ERROR, exc.toString());
553 return relationshipList;
557 * Adds the relationship list.
559 * @param relationshipList the relationship list
561 public void addRelationshipList(RelationshipList relationshipList) {
563 if (!relationshipLists.contains(relationshipList)) {
564 relationshipLists.add(relationshipList);
570 * Process pathed self link response.
572 * @param selfLinkJsonResponse the self link json response
573 * @param startNodeType the start node type
574 * @param startNodeResourceKey the start node resource key
576 public void processPathedSelfLinkResponse(String selfLinkJsonResponse, String startNodeType,
577 String startNodeResourceKey) {
579 if (selfLinkJsonResponse == null || selfLinkJsonResponse.length() == 0) {
580 LOG.error(AaiUiMsgs.SELF_LINK_NULL_EMPTY_RESPONSE);
585 JsonNode jsonNode = mapper.readValue(selfLinkJsonResponse, JsonNode.class);
587 Iterator<Entry<String, JsonNode>> fieldNames = jsonNode.fields();
588 Entry<String, JsonNode> field = null;
590 while (fieldNames.hasNext()) {
592 field = fieldNames.next();
595 * Is there a way to tell if the field is an aggregate or an atomic value? This is where our
596 * flattening code needs to live
599 String fieldName = field.getKey();
601 if ("relationship-list".equals(fieldName)) {
604 * Parse the relationship list like we were doing before, so we can determine whether or
605 * not to keep it or traverse it after we have performed the evaluative node depth logic.
607 RelationshipList relationshipList =
608 analyzeSelfLinkRelationshipList(field.getValue().toString());
610 if (relationshipList != null) {
611 this.relationshipLists.add(relationshipList);
613 LOG.info(AaiUiMsgs.NO_RELATIONSHIP_DISCOVERED, nodeId);
616 JsonNode nodeValue = field.getValue();
618 if (nodeValue != null && nodeValue.isValueNode()) {
621 * before we blindly add the fieldName and value to our property set, let's do one more
622 * check to see if the field name is an entity type. If it is, then our complex
623 * attribute processing code will pick it up and process it instead, but this is
624 * probably more likely just for array node types, but we'll see.
627 if (oxmModelLoader.getEntityDescriptor(fieldName) == null) {
629 * this is no an entity type as far as we can tell, so we can add it to our property
633 addProperty(fieldName, nodeValue.asText());
639 if (nodeValue.isArray()) {
642 * make sure array entity-type collection is not an entityType before adding it to the
643 * property set. The expetation is that it will be added the visualization through a
644 * complex group or relationship.
647 if (oxmModelLoader.getEntityDescriptor(field.getKey()) == null) {
649 * this is no an entity type as far as we can tell, so we can add it to our property
653 addProperty(field.getKey(), nodeValue.toString());
659 complexGroups.add(nodeValue);
669 } catch (IOException exc) {
670 LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "POJO", exc.getLocalizedMessage());
671 this.setProcessingErrorOccurred(true);
673 "An error occurred while converting JSON into POJO = " + exc.getLocalizedMessage());
678 public void setSelfLink(String selfLink) {
679 this.selfLink = selfLink;
683 * Adds the complex group.
685 * @param complexGroup the complex group
687 public void addComplexGroup(JsonNode complexGroup) {
689 if (!complexGroups.contains(complexGroup)) {
690 complexGroups.add(complexGroup);
698 * @param level the level
699 * @param paddingString the padding string
700 * @return the padding
702 private static String getPadding(int level, String paddingString) {
703 StringBuilder sb = new StringBuilder(32);
704 for (int x = 0; x < level; x++) {
705 sb.append(paddingString);
707 return sb.toString();
713 * @param showProperties the show properties
716 public String dumpNodeTree(boolean showProperties) {
717 return dumpNodeTree(0, showProperties);
723 * @param level the level
724 * @param showProperties the show properties
727 private String dumpNodeTree(int level, boolean showProperties) {
728 StringBuilder sb = new StringBuilder(128);
729 String padding = getPadding(level, " ");
731 sb.append(padding + " -> " + getNodeId() + "]").append("\n");
732 sb.append(padding + " -> primaryKeyName = " + primaryKeyName + "]").append("\n");
733 sb.append(padding + " -> primaryKeyValue = " + primaryKeyValue + "]").append("\n");
734 sb.append(padding + " -> entityType = " + entityType + "]").append("\n");
736 if (showProperties) {
737 Set<Entry<String, String>> entries = properties.entrySet();
738 for (Entry<String, String> entry : entries) {
740 padding + " ----> " + String.format("[ %s => %s ]", entry.getKey(), entry.getValue()))
745 sb.append(padding + " ----> " + String.format("[ selfLink => %s ]", getSelfLink()))
748 sb.append("\n").append(padding + " ----> Inbound Neighbors:").append("\n");
750 for (String inboundNeighbor : inboundNeighbors) {
751 sb.append("\n").append(inboundNeighbor.toString());
754 sb.append(padding + " ----> Outbound Neighbors:").append("\n");
755 sb.append("\n").append(padding + " ----> Outbound Neighbors:").append("\n");
757 for (String outboundNeighbor : outboundNeighbors) {
758 sb.append("\n").append(outboundNeighbor.toString());
761 return sb.toString();
765 public String getProcessingErrorCauses() {
767 StringBuilder sb = new StringBuilder(128);
769 for (String c : this.errorCauses) {
770 sb.append(c).append("\n");
773 return sb.toString();