/* * Copyright 2014 NAVER Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.navercorp.pinpoint.web.applicationmap; import com.navercorp.pinpoint.common.trace.ServiceType; import com.navercorp.pinpoint.common.server.util.AgentLifeCycleState; import com.navercorp.pinpoint.web.applicationmap.histogram.AgentTimeHistogram; import com.navercorp.pinpoint.web.applicationmap.histogram.AgentTimeHistogramBuilder; import com.navercorp.pinpoint.web.applicationmap.histogram.ApplicationTimeHistogram; import com.navercorp.pinpoint.web.applicationmap.histogram.ApplicationTimeHistogramBuilder; import com.navercorp.pinpoint.web.applicationmap.histogram.Histogram; import com.navercorp.pinpoint.web.applicationmap.histogram.NodeHistogram; import com.navercorp.pinpoint.web.applicationmap.rawdata.AgentHistogram; import com.navercorp.pinpoint.web.applicationmap.rawdata.AgentHistogramList; import com.navercorp.pinpoint.web.applicationmap.rawdata.LinkCallDataMap; import com.navercorp.pinpoint.web.applicationmap.rawdata.LinkData; import com.navercorp.pinpoint.web.applicationmap.rawdata.LinkDataDuplexMap; import com.navercorp.pinpoint.web.applicationmap.rawdata.LinkDataMap; import com.navercorp.pinpoint.web.dao.MapResponseDao; import com.navercorp.pinpoint.web.service.AgentInfoService; import com.navercorp.pinpoint.web.vo.AgentInfo; import com.navercorp.pinpoint.web.vo.AgentStatus; import com.navercorp.pinpoint.web.vo.Application; import com.navercorp.pinpoint.web.vo.LinkKey; import com.navercorp.pinpoint.web.vo.Range; import com.navercorp.pinpoint.web.vo.ResponseHistogramBuilder; import com.navercorp.pinpoint.web.vo.ResponseTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * @author emeroad * @author minwoo.jung * @author HyunGil Jeong */ public class ApplicationMapBuilder { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Range range; public ApplicationMapBuilder(Range range) { if (range == null) { throw new NullPointerException("range must not be null"); } this.range = range; } /** * Returns an application map with a single node containing the application's agents that were running. */ public ApplicationMap build(Application application, AgentInfoService agentInfoService) { NodeList nodeList = new NodeList(); LinkList emptyLinkList = new LinkList(); Node node = new Node(application); Set<AgentInfo> agentInfos = agentInfoService.getAgentsByApplicationName(application.getName(), range.getTo()); Set<AgentInfo> runningAgents = new HashSet<>(); for (AgentInfo agentInfo : agentInfos) { if (isAgentRunning(agentInfo)) { runningAgents.add(agentInfo); } } if (runningAgents.isEmpty()) { return new DefaultApplicationMap(range, nodeList, emptyLinkList); } else { ServerBuilder serverBuilder = new ServerBuilder(); serverBuilder.addAgentInfo(runningAgents); ServerInstanceList serverInstanceList = serverBuilder.build(); node.setServerInstanceList(serverInstanceList); node.setNodeHistogram(new NodeHistogram(application, range)); nodeList.addNode(node); return new DefaultApplicationMap(range, nodeList, emptyLinkList); } } public ApplicationMap build(LinkDataDuplexMap linkDataDuplexMap, AgentInfoPopulator agentInfoPopulator, NodeHistogramDataSource nodeHistogramDataSource) { if (linkDataDuplexMap == null) { throw new NullPointerException("linkDataMap must not be null"); } if (agentInfoPopulator == null) { throw new NullPointerException("agentInfoPopulator must not be null"); } NodeList nodeList = buildNode(linkDataDuplexMap); LinkList linkList = buildLink(nodeList, linkDataDuplexMap); appendNodeResponseTime(nodeList, linkList, nodeHistogramDataSource); appendAgentInfo(nodeList, linkDataDuplexMap, agentInfoPopulator); final ApplicationMap map = new DefaultApplicationMap(range, nodeList, linkList); return map; } public ApplicationMap build(LinkDataDuplexMap linkDataDuplexMap, final AgentInfoService agentInfoService, final MapResponseDao mapResponseDao) { AgentInfoPopulator agentInfoPopulator = new AgentInfoPopulator() { @Override public void addAgentInfos(Node node) { ServerInstanceList serverInstanceList = getServerInstanceList(node, agentInfoService); node.setServerInstanceList(serverInstanceList); } }; NodeHistogramDataSource responseSource = new NodeHistogramDataSource() { @Override public NodeHistogram createNodeHistogram(Application application) { final List<ResponseTime> responseHistogram = mapResponseDao.selectResponseTime(application, range); final NodeHistogram nodeHistogram = new NodeHistogram(application, range, responseHistogram); return nodeHistogram; } }; return this.build(linkDataDuplexMap, agentInfoPopulator, responseSource); } public ApplicationMap build(NodeList nodeList, LinkList linkList) { return new DefaultApplicationMap(range, nodeList, linkList); } private ServerInstanceList getServerInstanceList(final Node node, final AgentInfoService agentInfoService) { long timestamp = range.getTo(); if (timestamp < 0) { return new ServerInstanceList(); } Set<AgentInfo> agentList = agentInfoService.getAgentsByApplicationNameWithoutStatus(node.getApplication().getName(), timestamp); if (agentList.isEmpty()) { logger.warn("agentInfo not found. applicationName:{}", node.getApplication()); // avoid NPE return new ServerInstanceList(); } logger.debug("add agentInfo. {}, {}", node.getApplication(), agentList); ServerBuilder builder = new ServerBuilder(); // agentSet exists if the destination is a WAS, and has agent installed agentList = filterAgentInfoByResponseData(agentList, timestamp, node, agentInfoService); builder.addAgentInfo(agentList); ServerInstanceList serverInstanceList = builder.build(); return serverInstanceList; } public ApplicationMap build(LinkDataDuplexMap linkDataDuplexMap, final AgentInfoService agentInfoService, final ResponseHistogramBuilder mapHistogramSummary) { AgentInfoPopulator agentInfoPopulator = new AgentInfoPopulator() { @Override public void addAgentInfos(Node node) { ServerInstanceList serverInstanceList = getServerInstanceList(node, agentInfoService); node.setServerInstanceList(serverInstanceList); } }; NodeHistogramDataSource responseSource = new NodeHistogramDataSource() { @Override public NodeHistogram createNodeHistogram(Application application) { List<ResponseTime> responseHistogram = mapHistogramSummary.getResponseTimeList(application); final NodeHistogram nodeHistogram = new NodeHistogram(application, range, responseHistogram); return nodeHistogram; } }; return this.build(linkDataDuplexMap, agentInfoPopulator, responseSource); } public interface NodeHistogramDataSource { NodeHistogram createNodeHistogram(Application application); } public interface AgentInfoPopulator { void addAgentInfos(Node node); } private NodeList buildNode(LinkDataDuplexMap linkDataDuplexMap) { NodeList nodeList = new NodeList(); createNode(nodeList, linkDataDuplexMap.getSourceLinkDataMap()); logger.debug("node size:{}", nodeList.size()); createNode(nodeList, linkDataDuplexMap.getTargetLinkDataMap()); logger.debug("node size:{}", nodeList.size()); logger.debug("allNode:{}", nodeList.getNodeList()); return nodeList; } private void createNode(NodeList nodeList, LinkDataMap linkDataMap) { for (LinkData linkData : linkDataMap.getLinkDataList()) { final Application fromApplication = linkData.getFromApplication(); // FROM is either a CLIENT or a node // cannot be RPC. Already converted to unknown. if (!fromApplication.getServiceType().isRpcClient()) { final boolean success = addNode(nodeList, fromApplication); if (success) { logger.debug("createSourceNode:{}", fromApplication); } } else { logger.warn("found rpc fromNode linkData:{}", linkData); } final Application toApplication = linkData.getToApplication(); // FROM -> TO : TO is either a CLIENT or a node if (!toApplication.getServiceType().isRpcClient()) { final boolean success = addNode(nodeList, toApplication); if (success) { logger.debug("createTargetNode:{}", toApplication); } } else { logger.warn("found rpc toNode:{}", linkData); } } } private boolean addNode(NodeList nodeList, Application application) { if (nodeList.containsNode(application)) { return false; } Node fromNode = new Node(application); return nodeList.addNode(fromNode); } private LinkList buildLink(NodeList nodeList, LinkDataDuplexMap linkDataDuplexMap) { // don't change LinkList linkList = new LinkList(); createSourceLink(nodeList, linkList, linkDataDuplexMap.getSourceLinkDataMap()); logger.debug("link size:{}", linkList.size()); createTargetLink(nodeList, linkList, linkDataDuplexMap.getTargetLinkDataMap()); logger.debug("link size:{}", linkList.size()); for (Link link : linkList.getLinkList()) { appendLinkHistogram(link, linkDataDuplexMap); } return linkList; } private void appendLinkHistogram(Link link, LinkDataDuplexMap linkDataDuplexMap) { logger.debug("appendLinkHistogram link:{}", link); LinkKey key = link.getLinkKey(); LinkData sourceLinkData = linkDataDuplexMap.getSourceLinkData(key); if (sourceLinkData != null) { link.addSource(sourceLinkData.getLinkCallDataMap()); } LinkData targetLinkData = linkDataDuplexMap.getTargetLinkData(key); if (targetLinkData != null) { link.addTarget(targetLinkData.getLinkCallDataMap()); } } private void createSourceLink(NodeList nodeList, LinkList linkList, LinkDataMap linkDataMap) { for (LinkData linkData : linkDataMap.getLinkDataList()) { final Application fromApplicationId = linkData.getFromApplication(); Node fromNode = nodeList.findNode(fromApplicationId); final Application toApplicationId = linkData.getToApplication(); Node toNode = nodeList.findNode(toApplicationId); // rpc client missing if (toNode == null) { logger.warn("toNode rcp client not found:{}", toApplicationId); continue; } // for RPC clients: skip if there is a dest application, convert to "unknown cloud" if not // shouldn't really be necessary as rpc client toNodes are converted to unknown nodes beforehand. if (toNode.getServiceType().isRpcClient()) { if (!nodeList.containsNode(toNode.getApplication())) { final Link link = addLink(linkList, fromNode, toNode, CreateType.Source); if (link != null) { logger.debug("createRpcSourceLink:{}", link); } } } else { final Link link = addLink(linkList, fromNode, toNode, CreateType.Source); if (link != null) { logger.debug("createSourceLink:{}", link); } } } } private Link addLink(LinkList linkList, Node fromNode, Node toNode, CreateType createType) { final Link link = new Link(createType, fromNode, toNode, range); if (linkList.addLink(link)) { return link; } else { return null; } } private void createTargetLink(NodeList nodeList, LinkList linkList, LinkDataMap linkDataMap) { for (LinkData linkData : linkDataMap.getLinkDataList()) { final Application fromApplicationId = linkData.getFromApplication(); Node fromNode = nodeList.findNode(fromApplicationId); final Application toApplicationId = linkData.getToApplication(); Node toNode = nodeList.findNode(toApplicationId); // rpc client missing if (fromNode == null) { logger.warn("fromNode rcp client not found:{}", toApplicationId); continue; } // for RPC clients: skip if there is a dest application, convert to "unknown cloud" if not if (toNode.getServiceType().isRpcClient()) { // check if "to" node exists if (!nodeList.containsNode(toNode.getApplication())) { final Link link = addLink(linkList, fromNode, toNode, CreateType.Target); if (link != null) { logger.debug("createRpcTargetLink:{}", link); } } } else { final Link link = addLink(linkList, fromNode, toNode, CreateType.Target); if (link != null) { logger.debug("createTargetLink:{}", link); } } } } public void appendNodeResponseTime(NodeList nodeList, LinkList linkList, NodeHistogramDataSource nodeHistogramDataSource) { if (nodeHistogramDataSource == null) { throw new NullPointerException("nodeHistogramDataSource must not be null"); } final Collection<Node> nodes = nodeList.getNodeList(); for (Node node : nodes) { final ServiceType nodeType = node.getServiceType(); if (nodeType.isWas()) { // for WAS nodes, set their own response time histogram final Application wasNode = node.getApplication(); final NodeHistogram nodeHistogram = nodeHistogramDataSource.createNodeHistogram(wasNode); node.setNodeHistogram(nodeHistogram); } else if (nodeType.isTerminal() || nodeType.isUnknown()) { final NodeHistogram nodeHistogram = createTerminalNodeHistogram(node, linkList); node.setNodeHistogram(nodeHistogram); } else if (nodeType.isQueue()) { // Virtual queue node - queues with agent installed will be handled above as a WAS node final NodeHistogram nodeHistogram = createTerminalNodeHistogram(node, linkList); node.setNodeHistogram(nodeHistogram); } else if (nodeType.isUser()) { // for User nodes, find its source link and create the histogram Application userNode = node.getApplication(); final NodeHistogram nodeHistogram = new NodeHistogram(userNode, range); final List<Link> fromLink = linkList.findFromLink(userNode); if (fromLink.size() > 1) { // used first(0) link. logger.warn("Invalid from UserNode:{}", linkList.getLinkList()); } else if (fromLink.isEmpty()) { logger.warn("from UserNode not found:{}", userNode); continue; } final Link sourceLink = fromLink.get(0); nodeHistogram.setApplicationHistogram(sourceLink.getHistogram()); ApplicationTimeHistogram histogramData = sourceLink.getTargetApplicationTimeSeriesHistogramData(); nodeHistogram.setApplicationTimeHistogram(histogramData); node.setNodeHistogram(nodeHistogram); } else { // dummy data NodeHistogram dummy = new NodeHistogram(node.getApplication(), range); node.setNodeHistogram(dummy); } } } private NodeHistogram createTerminalNodeHistogram(Node node, LinkList linkList) { // for Terminal nodes, add all links pointing to iself and create the histogram final Application nodeApplication = node.getApplication(); final NodeHistogram nodeHistogram = new NodeHistogram(nodeApplication, range); // create applicationHistogram final List<Link> toLinkList = linkList.findToLink(nodeApplication); final Histogram applicationHistogram = new Histogram(node.getServiceType()); for (Link link : toLinkList) { applicationHistogram.add(link.getHistogram()); } nodeHistogram.setApplicationHistogram(applicationHistogram); // create applicationTimeHistogram LinkCallDataMap linkCallDataMap = new LinkCallDataMap(); for (Link link : toLinkList) { LinkCallDataMap sourceLinkCallDataMap = link.getSourceLinkCallDataMap(); linkCallDataMap.addLinkDataMap(sourceLinkCallDataMap); } ApplicationTimeHistogramBuilder builder = new ApplicationTimeHistogramBuilder(nodeApplication, range); ApplicationTimeHistogram applicationTimeHistogram = builder.build(linkCallDataMap.getLinkDataList()); nodeHistogram.setApplicationTimeHistogram(applicationTimeHistogram); // for Terminal nodes, create AgentLevel histogram if (nodeApplication.getServiceType().isTerminal() || nodeApplication.getServiceType().isQueue()) { final Map<String, Histogram> agentHistogramMap = new HashMap<>(); for (Link link : toLinkList) { LinkCallDataMap sourceLinkCallDataMap = link.getSourceLinkCallDataMap(); AgentHistogramList targetList = sourceLinkCallDataMap.getTargetList(); for (AgentHistogram histogram : targetList.getAgentHistogramList()) { Histogram find = agentHistogramMap.get(histogram.getId()); if (find == null) { find = new Histogram(histogram.getServiceType()); agentHistogramMap.put(histogram.getId(), find); } find.add(histogram.getHistogram()); } nodeHistogram.setAgentHistogramMap(agentHistogramMap); } } LinkCallDataMap mergeSource = new LinkCallDataMap(); for (Link link : toLinkList) { LinkCallDataMap sourceLinkCallDataMap = link.getSourceLinkCallDataMap(); mergeSource.addLinkDataMap(sourceLinkCallDataMap); } AgentTimeHistogramBuilder agentTimeBuilder = new AgentTimeHistogramBuilder(nodeApplication, range); AgentTimeHistogram agentTimeHistogram = agentTimeBuilder.buildTarget(mergeSource); nodeHistogram.setAgentTimeHistogram(agentTimeHistogram); return nodeHistogram; } public void appendAgentInfo(NodeList nodeList, LinkDataDuplexMap linkDataDuplexMap, AgentInfoPopulator agentInfoPopulator) { for (Node node : nodeList.getNodeList()) { appendServerInfo(node, linkDataDuplexMap, agentInfoPopulator); } } private void appendServerInfo(Node node, LinkDataDuplexMap linkDataDuplexMap, AgentInfoPopulator agentInfoPopulator) { final ServiceType nodeServiceType = node.getServiceType(); if (nodeServiceType.isUnknown()) { // we do not know the server info for unknown nodes return; } if (nodeServiceType.isWas()) { agentInfoPopulator.addAgentInfos(node); } else if (nodeServiceType.isTerminal() || nodeServiceType.isQueue()) { // extract information about the terminal node ServerBuilder builder = new ServerBuilder(); for (LinkData linkData : linkDataDuplexMap.getSourceLinkDataList()) { Application toApplication = linkData.getToApplication(); if (node.getApplication().equals(toApplication)) { builder.addCallHistogramList(linkData.getTargetList()); } } ServerInstanceList serverInstanceList = builder.build(); node.setServerInstanceList(serverInstanceList); } else { // add empty information node.setServerInstanceList(new ServerInstanceList()); } } /** * Filters AgentInfo by whether they actually have response data. * For agents that do not have response data, check their status and include those that were alive. */ private Set<AgentInfo> filterAgentInfoByResponseData(Set<AgentInfo> agentList, long timestamp, Node node, AgentInfoService agentInfoService) { Set<AgentInfo> filteredAgentInfo = new HashSet<>(); NodeHistogram nodeHistogram = node.getNodeHistogram(); Map<String, Histogram> agentHistogramMap = nodeHistogram.getAgentHistogramMap(); for (AgentInfo agentInfo : agentList) { String agentId = agentInfo.getAgentId(); if (agentHistogramMap.containsKey(agentId)) { filteredAgentInfo.add(agentInfo); } else { AgentStatus status = agentInfoService.getAgentStatus(agentId, timestamp); agentInfo.setStatus(status); if (isAgentRunning(agentInfo)) { filteredAgentInfo.add(agentInfo); } } } return filteredAgentInfo; } private boolean isAgentRunning(AgentInfo agentInfo) { if (agentInfo.getStatus() != null) { return agentInfo.getStatus().getState() == AgentLifeCycleState.RUNNING; } else { return false; } } }