/* * Copyright 2008-2017 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 static net.bull.javamelody.HttpParameters.DEFAULT_WITH_CURRENT_REQUESTS_PART; import static net.bull.javamelody.HttpParameters.PART_PARAMETER; import java.io.IOException; import java.io.Serializable; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import net.bull.javamelody.SamplingProfiler.SampledMethod; /** * Collecteur de données pour une application sur un ou plusieurs serveur(s) distant(s) : utilisé par serveur de collecte et par IHM Swing. * @author Emeric Vernat */ class RemoteCollector { private final String application; private List<URL> urls; private Collector collector; private List<JavaInformations> javaInformationsList; private Map<JavaInformations, List<CounterRequestContext>> currentRequests; private String cookies; private boolean aggregationDisabled; /** * Constructeur. * @param application Nom de l'application * @param urls URLs */ RemoteCollector(String application, List<URL> urls) { super(); assert application != null; assert urls != null; this.application = application; this.urls = urls; } String collectData() throws IOException { return collectDataWithUrls(urls); } String collectDataIncludingCurrentRequests() throws IOException { final List<URL> urlsWithCurrentRequests = new ArrayList<URL>(); for (final URL url : urls) { urlsWithCurrentRequests.add(new URL(url.toString() + '&' + PART_PARAMETER + '=' + DEFAULT_WITH_CURRENT_REQUESTS_PART)); } return collectDataWithUrls(urlsWithCurrentRequests); } private String collectDataWithUrls(List<URL> urlsForCollect) throws IOException { final List<JavaInformations> javaInfosList = new ArrayList<JavaInformations>(); final Map<JavaInformations, List<CounterRequestContext>> counterRequestContextsByJavaInformations = new HashMap<JavaInformations, List<CounterRequestContext>>(); final StringBuilder sb = new StringBuilder(); for (final URL url : urlsForCollect) { final List<Counter> counters = new ArrayList<Counter>(); final List<Serializable> serialized = createRemoteCall(url).collectData(); dispatchSerializables(serialized, counters, javaInfosList, counterRequestContextsByJavaInformations, sb); if (this.collector == null || aggregationDisabled) { this.collector = new Collector(application, counters); } else { addRequestsAndErrors(counters); } } this.javaInformationsList = javaInfosList; this.currentRequests = counterRequestContextsByJavaInformations; final String messageForReport; if (sb.length() == 0) { messageForReport = null; } else { messageForReport = sb.toString(); } return messageForReport; } private void dispatchSerializables(List<Serializable> serialized, List<Counter> counters, List<JavaInformations> javaInfosList, Map<JavaInformations, List<CounterRequestContext>> counterRequestContextsByJavaInformations, StringBuilder sb) { JavaInformations latestJavaInformations = null; final List<CounterRequestContext> counterRequestContextsList = new ArrayList<CounterRequestContext>(); for (final Serializable serializable : serialized) { if (serializable instanceof Counter) { final Counter counter = (Counter) serializable; counter.setApplication(application); counters.add(counter); } else if (serializable instanceof JavaInformations) { final JavaInformations newJavaInformations = (JavaInformations) serializable; latestJavaInformations = newJavaInformations; javaInfosList.add(newJavaInformations); } else if (serializable instanceof String) { sb.append(serializable).append('\n'); } else if (serializable instanceof CounterRequestContext) { final CounterRequestContext counterRequestContext = (CounterRequestContext) serializable; counterRequestContextsList.add(counterRequestContext); } } if (!counterRequestContextsList.isEmpty()) { counterRequestContextsByJavaInformations.put(latestJavaInformations, counterRequestContextsList); } } String executeActionAndCollectData(Action action, String counterName, String sessionId, String threadId, String jobId, String cacheId) throws IOException { assert action != null; final List<URL> actionUrls = new ArrayList<URL>(urls.size()); for (final URL url : urls) { final URL actionUrl = createRemoteCall(url).getActionUrl(action, counterName, sessionId, threadId, jobId, cacheId); actionUrls.add(actionUrl); } return collectDataWithUrls(actionUrls); } List<SessionInformations> collectSessionInformations(String sessionId) throws IOException { // sessionId est null si on veut toutes les sessions if (sessionId == null) { // récupération à la demande des sessions final List<SessionInformations> sessionsInformations = new ArrayList<SessionInformations>(); for (final URL url : urls) { final List<SessionInformations> sessions = createRemoteCall(url) .collectSessionInformations(null); sessionsInformations.addAll(sessions); } SessionListener.sortSessions(sessionsInformations); return sessionsInformations; } for (final URL url : urls) { final List<SessionInformations> sessions = createRemoteCall(url) .collectSessionInformations(sessionId); if (!sessions.isEmpty()) { return sessions; } } // session non trouvée, alors la session a été invalidée return Collections.emptyList(); } List<SampledMethod> collectHotspots() throws IOException { // récupération à la demande des hotspots final Map<SampledMethod, SampledMethod> map = new HashMap<SampledMethod, SampledMethod>(); for (final URL url : urls) { final List<SampledMethod> hotspots = createRemoteCall(url).collectHotspots(); if (urls.size() == 1) { // s'il n'y a qu'un serveur, inutile d'aller plus loin pour fusionner les données return hotspots; } for (final SampledMethod method : hotspots) { // SampledMethod implémente hashCode et equals final SampledMethod previous = map.get(method); if (previous == null) { map.put(method, method); } else { previous.setCount(previous.getCount() + method.getCount()); } } } final List<SampledMethod> hotspots = new ArrayList<SampledMethod>(map.values()); Collections.sort(hotspots); return hotspots; } HeapHistogram collectHeapHistogram() throws IOException { // récupération à la demande des HeapHistogram HeapHistogram heapHistoTotal = null; for (final URL url : urls) { final HeapHistogram heapHisto = createRemoteCall(url).collectHeapHistogram(); if (heapHistoTotal == null) { heapHistoTotal = heapHisto; } else { heapHistoTotal.add(heapHisto); } } return heapHistoTotal; } DatabaseInformations collectDatabaseInformations(int requestIndex) throws IOException { final URL url = urls.get(0); return createRemoteCall(url).collectDatabaseInformations(requestIndex); } List<List<ConnectionInformations>> collectConnectionInformations() throws IOException { // récupération à la demande des connections final List<List<ConnectionInformations>> connectionInformations = new ArrayList<List<ConnectionInformations>>(); for (final URL url : urls) { final List<List<ConnectionInformations>> connections = createRemoteCall(url) .collectConnectionInformations(); connectionInformations.addAll(connections); } return connectionInformations; } Map<String, List<ProcessInformations>> collectProcessInformations() throws IOException { // récupération à la demande des processus final Map<String, List<ProcessInformations>> result = new LinkedHashMap<String, List<ProcessInformations>>(); for (final URL url : urls) { final Map<String, List<ProcessInformations>> processesByTitle = createRemoteCall(url) .collectProcessInformations(); result.putAll(processesByTitle); } return result; } List<JndiBinding> collectJndiBindings(String path) throws IOException { // récupération à la demande des bindings JNDI, // contrairement aux requêtes en cours ou aux processus, un serveur de l'application suffira // car l'arbre JNDI est en général identique dans tout l'éventuel cluster final URL url = urls.get(0); return createRemoteCall(url).collectJndiBindings(path); } Map<String, List<MBeanNode>> collectMBeans() throws IOException { // récupération à la demande des MBeans final Map<String, List<MBeanNode>> result = new LinkedHashMap<String, List<MBeanNode>>(); for (final URL url : urls) { final Map<String, List<MBeanNode>> mbeansByTitle = createRemoteCall(url) .collectMBeans(); result.putAll(mbeansByTitle); } return result; } Map<String, MavenArtifact> collectWebappDependencies() throws IOException { // récupération à la demande des dépendances, // contrairement aux requêtes en cours ou aux processus, un serveur de l'application suffira // car le résultat est identique dans tout l'éventuel cluster final URL url = urls.get(0); return createRemoteCall(url).collectWebappDependencies(); } Map<String, Date> collectWebappVersions() throws IOException { final URL url = urls.get(0); return createRemoteCall(url).collectWebappVersions(); } Map<JavaInformations, List<CounterRequestContext>> collectCurrentRequests() throws IOException { // récupération à la demande des requêtes en cours final Map<JavaInformations, List<CounterRequestContext>> result = new LinkedHashMap<JavaInformations, List<CounterRequestContext>>(); for (final URL url : urls) { final Map<JavaInformations, List<CounterRequestContext>> requests = createRemoteCall( url).collectCurrentRequests(); result.putAll(requests); } return result; } List<List<ThreadInformations>> getThreadInformationsLists() { final List<List<ThreadInformations>> result = new ArrayList<List<ThreadInformations>>(); for (final JavaInformations javaInformations : this.javaInformationsList) { result.add(new ArrayList<ThreadInformations>( javaInformations.getThreadInformationsList())); } return result; } Map<String, byte[]> collectJRobins(int width, int height) throws IOException { final URL url = urls.get(0); return createRemoteCall(url).collectJRobins(width, height); } Map<String, byte[]> collectOtherJRobins(int width, int height) throws IOException { final URL url = urls.get(0); return createRemoteCall(url).collectOtherJRobins(width, height); } byte[] collectJRobin(String graphName, int width, int height) throws IOException { final URL url = urls.get(0); return createRemoteCall(url).collectJRobin(graphName, width, height); } String collectSqlRequestExplainPlan(String sqlRequest) throws IOException { final URL url = urls.get(0); return createRemoteCall(url).collectSqlRequestExplainPlan(sqlRequest); } private void addRequestsAndErrors(List<Counter> counters) { for (final Counter newCounter : counters) { final Counter counter = collector.getCounterByName(newCounter.getName()); // counter.isDisplayed() peut changer pour spring, ejb ou services selon l'utilisation counter.setDisplayed(newCounter.isDisplayed()); counter.addRequestsAndErrors(newCounter); } } private RemoteCall createRemoteCall(URL url) { final RemoteCall remoteCall = new RemoteCall(url); remoteCall.setCookies(cookies); return remoteCall; } static String getHostAndPort(URL url) { if (url.getPort() != -1) { return url.getHost() + ':' + url.getPort(); } // port est -1 si c'est le port par défaut (80) return url.getHost(); } String getApplication() { return application; } List<URL> getURLs() { return urls; } Collector getCollector() { return collector; } List<JavaInformations> getJavaInformationsList() { return javaInformationsList; } Map<JavaInformations, List<CounterRequestContext>> getCurrentRequests() { return currentRequests; } // cette méthode est utilisée dans l'ihm Swing void setURLs(List<URL> newURLs) { assert urls != null; this.urls = newURLs; } // cette méthode est utilisée dans l'ihm Swing void setCookies(String cookies) { // cookies peut être null this.cookies = cookies; } // cette méthode est utilisée dans l'ihm Swing void disableAggregation() { aggregationDisabled = true; } }