/* * 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.service; import com.google.common.collect.Ordering; import com.navercorp.pinpoint.web.dao.stat.JvmGcDao; import com.navercorp.pinpoint.web.vo.Application; import com.navercorp.pinpoint.web.vo.Range; import org.apache.commons.collections.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import com.navercorp.pinpoint.web.dao.ApplicationIndexDao; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; /** * @author netspider * @author HyunGil Jeong */ @Service public class AdminServiceImpl implements AdminService { private static final int MIN_DURATION_DAYS_FOR_INACTIVITY = 30; private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private ApplicationIndexDao applicationIndexDao; @Autowired @Qualifier("jvmGcDaoFactory") private JvmGcDao jvmGcDao; @Override public void removeApplicationName(String applicationName) { applicationIndexDao.deleteApplicationName(applicationName); } @Override public void removeAgentId(String applicationName, String agentId) { applicationIndexDao.deleteAgentId(applicationName, agentId); } @Override public void removeInactiveAgents(int durationDays) { if (durationDays < MIN_DURATION_DAYS_FOR_INACTIVITY) { throw new IllegalArgumentException("duration may not be less than " + MIN_DURATION_DAYS_FOR_INACTIVITY + " days"); } Map<String, List<String>> inactiveAgentMap = new TreeMap<>(Ordering.usingToString()); List<Application> applications = this.applicationIndexDao.selectAllApplicationNames(); Set<String> applicationNames = new TreeSet<>(Ordering.usingToString()); // remove duplicates (same application name but different service type) for (Application application : applications) { applicationNames.add(application.getName()); } for (String applicationName : applicationNames) { List<String> agentIds = this.applicationIndexDao.selectAgentIds(applicationName); Collections.sort(agentIds); List<String> inactiveAgentIds = filterInactiveAgents(agentIds, durationDays); if (!CollectionUtils.isEmpty(inactiveAgentIds)) { inactiveAgentMap.put(applicationName, inactiveAgentIds); } } // map may become big, but realistically won't cause OOM // if it becomes an issue, consider deleting inside the loop above logger.info("deleting {}", inactiveAgentMap); this.applicationIndexDao.deleteAgentIds(inactiveAgentMap); } @Override public Map<String, List<Application>> getAgentIdMap() { Map<String, List<Application>> agentIdMap = new TreeMap<>(Ordering.usingToString()); List<Application> applications = this.applicationIndexDao.selectAllApplicationNames(); for (Application application : applications) { List<String> agentIds = this.applicationIndexDao.selectAgentIds(application.getName()); for (String agentId : agentIds) { if (!agentIdMap.containsKey(agentId)) { agentIdMap.put(agentId, new ArrayList<Application>()); } agentIdMap.get(agentId).add(application); } } return agentIdMap; } @Override public Map<String, List<Application>> getDuplicateAgentIdMap() { Map<String, List<Application>> duplicateAgentIdMap = new TreeMap<>(Ordering.usingToString()); Map<String, List<Application>> agentIdMap = this.getAgentIdMap(); for (Map.Entry<String, List<Application>> entry : agentIdMap.entrySet()) { String agentId = entry.getKey(); List<Application> applications = entry.getValue(); if (applications.size() > 1) { duplicateAgentIdMap.put(agentId, applications); } } return duplicateAgentIdMap; } @Override public Map<String, List<Application>> getInactiveAgents(String applicationName, int durationDays) { if (applicationName == null) { throw new NullPointerException("applicationName must not be null"); } if (durationDays < MIN_DURATION_DAYS_FOR_INACTIVITY) { throw new IllegalArgumentException("duration may not be less than " + MIN_DURATION_DAYS_FOR_INACTIVITY + " days"); } List<String> agentIds = this.applicationIndexDao.selectAgentIds(applicationName); if (CollectionUtils.isEmpty(agentIds)) { return Collections.emptyMap(); } Map<String, List<Application>> agentIdMap = this.getAgentIdMap(); Map<String, List<Application>> inactiveAgentMap = new TreeMap<>(Ordering.usingToString()); List<String> inactiveAgentIds = filterInactiveAgents(agentIds, durationDays); for (String inactiveAgentId : inactiveAgentIds) { List<Application> applications = agentIdMap.get(inactiveAgentId); inactiveAgentMap.put(inactiveAgentId, applications); } return inactiveAgentMap; } private List<String> filterInactiveAgents(List<String> agentIds, int durationDays) { if (CollectionUtils.isEmpty(agentIds)) { return Collections.emptyList(); } List<String> inactiveAgentIds = new ArrayList<>(); final long toTimestamp = System.currentTimeMillis(); Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, durationDays * -1); final long fromTimestamp = cal.getTimeInMillis(); Range queryRange = new Range(fromTimestamp, toTimestamp); for (String agentId : agentIds) { // FIXME This needs to be done with a more accurate information. // If at any time a non-java agent is introduced, or an agent that does not collect jvm data, // this will fail boolean dataExists = this.jvmGcDao.agentStatExists(agentId, queryRange); if (!dataExists) { inactiveAgentIds.add(agentId); } } return inactiveAgentIds; } }