/* * Copyright 2008-2014 by Emeric Vernat * * This file is part of Java Melody. * * 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 net.bull.javamelody; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import net.bull.javamelody.Counter.CounterRequestComparator; /** * Agrégation des requêtes d'un compteur pour l'affichage d'une synthèse. * @author Emeric Vernat */ class CounterRequestAggregation { private final Counter counter; private final List<CounterRequest> requests; private final CounterRequest globalRequest; private final int warningThreshold; private final int severeThreshold; private final boolean responseSizeDisplayed; private final boolean childHitsDisplayed; private final boolean timesDisplayed; private final boolean cpuTimesDisplayed; private final CounterRequest warningRequest; private final CounterRequest severeRequest; CounterRequestAggregation(Counter counter) { super(); assert counter != null; this.counter = counter; if (counter.isErrorCounter()) { this.requests = counter.getOrderedByHitsRequests(); } else { this.requests = counter.getOrderedRequests(); } assert requests != null; final String counterName = counter.getName(); this.globalRequest = new CounterRequest(counterName + " global", counterName); for (final CounterRequest request : requests) { // ici, pas besoin de synchronized sur request puisque ce sont des clones indépendants globalRequest.addHits(request); } // on n'affiche pas la colonne "Taille de réponse" si elle est négative car non défini // (pour les requêtes sql par exemple) this.responseSizeDisplayed = globalRequest.getResponseSizeMean() >= 0; this.childHitsDisplayed = globalRequest.hasChildHits(); this.timesDisplayed = globalRequest.getMean() >= 0; this.cpuTimesDisplayed = globalRequest.getCpuTimeMean() >= 0; // globalMean et globalStandardDeviation sont utilisées pour déterminer // les seuils des couleurs des moyennes dans le tableau quand les paramètres // warning-threshold-millis et severe-threshold-millis ne sont pas définis final int globalMean = globalRequest.getMean(); final int globalStandardDeviation = globalRequest.getStandardDeviation(); this.warningThreshold = getThreshold(Parameter.WARNING_THRESHOLD_MILLIS, globalMean + globalStandardDeviation); this.severeThreshold = getThreshold(Parameter.SEVERE_THRESHOLD_MILLIS, globalMean + 2 * globalStandardDeviation); // synthèse globale avec requêtes global, warning et severe // on calcule les pourcentages de requêtes dont les temps (moyens!) dépassent les 2 seuils this.warningRequest = new CounterRequest(counterName + " warning", counterName); this.severeRequest = new CounterRequest(counterName + " severe", counterName); for (final CounterRequest request : requests) { // ici, pas besoin de synchronized sur request puisque ce sont des clones indépendants final int mean = request.getMean(); if (mean > severeThreshold) { severeRequest.addHits(request); } else if (mean > warningThreshold) { warningRequest.addHits(request); } // les requêtes sous warning ne sont pas décomptées dans la synthèse autrement que dans global } } private static int getThreshold(Parameter parameter, int defaultValue) { final String param = Parameters.getParameter(parameter); if (param == null) { return defaultValue; } final int threshold = Integer.parseInt(param); if (threshold <= 0) { throw new IllegalStateException("Le paramètre " + parameter.getCode() + " doit être > 0"); } return threshold; } List<CounterRequest> getRequests() { return requests; } CounterRequest getGlobalRequest() { return globalRequest; } CounterRequest getWarningRequest() { return warningRequest; } CounterRequest getSevereRequest() { return severeRequest; } int getWarningThreshold() { return warningThreshold; } int getSevereThreshold() { return severeThreshold; } boolean isResponseSizeDisplayed() { return responseSizeDisplayed; } boolean isChildHitsDisplayed() { return childHitsDisplayed; } boolean isTimesDisplayed() { return timesDisplayed; } boolean isCpuTimesDisplayed() { return cpuTimesDisplayed; } List<CounterRequest> getRequestsAggregatedOrFilteredByClassName(String requestId) { final List<CounterRequest> requestsAggregatedByClassName = getRequestsAggregatedByClassName(); final List<CounterRequest> requestList; if (requestId == null) { // on va afficher la liste des requêtes aggrégées par classe requestList = requestsAggregatedByClassName; } else { // on a un paramètre requestId, ie que l'utilisateur a cliqué sur un lien de détail // des requêtes pour une classe, et on va afficher la liste des requêtes non aggrégées // mais filtrées pour cette classe requestList = new ArrayList<CounterRequest>(); // on recherche d'abord le nom de la classe à partir de requestId for (final CounterRequest requestAggregated : requestsAggregatedByClassName) { if (requestId.equals(requestAggregated.getId())) { final String className = requestAggregated.getName(); // et on filtre les requêtes pour cette classe requestList.addAll(getRequestsFilteredByClassName(className)); break; } } } return Collections.unmodifiableList(requestList); } private List<CounterRequest> getRequestsAggregatedByClassName() { assert counter.isBusinessFacadeCounter(); final Map<String, CounterRequest> requestMap = new HashMap<String, CounterRequest>(); final String counterName = counter.getName(); for (final CounterRequest request : getRequests()) { final String className = getClassNameFromRequest(request); CounterRequest global = requestMap.get(className); if (global == null) { global = new CounterRequest(className, counterName); requestMap.put(className, global); } global.addHits(request); } // on trie par la somme des durées final List<CounterRequest> requestList = new ArrayList<CounterRequest>(requestMap.values()); if (requestList.size() > 1) { Collections.sort(requestList, Collections.reverseOrder(new CounterRequestComparator())); } return requestList; } private List<CounterRequest> getRequestsFilteredByClassName(String className) { assert counter.isBusinessFacadeCounter(); assert className != null; final List<CounterRequest> requestList = new ArrayList<CounterRequest>(); for (final CounterRequest request : getRequests()) { final String requestClassName = getClassNameFromRequest(request); if (className.equals(requestClassName)) { requestList.add(request); } } if (requestList.size() > 1) { Collections.sort(requestList, Collections.reverseOrder(new CounterRequestComparator())); } return requestList; } private static String getClassNameFromRequest(CounterRequest request) { final int lastIndexOf = request.getName().lastIndexOf('.'); if (lastIndexOf != -1) { return request.getName().substring(0, lastIndexOf); } return request.getName(); } }