/*
* 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; // NOPMD
import static net.bull.javamelody.HttpParameters.CONNECTIONS_PART;
import static net.bull.javamelody.HttpParameters.CURRENT_REQUESTS_PART;
import static net.bull.javamelody.HttpParameters.DATABASE_PART;
import static net.bull.javamelody.HttpParameters.DEPENDENCIES_PART;
import static net.bull.javamelody.HttpParameters.EXPLAIN_PLAN_PART;
import static net.bull.javamelody.HttpParameters.GRAPH_PARAMETER;
import static net.bull.javamelody.HttpParameters.HEAP_HISTO_PART;
import static net.bull.javamelody.HttpParameters.HEIGHT_PARAMETER;
import static net.bull.javamelody.HttpParameters.HOTSPOTS_PART;
import static net.bull.javamelody.HttpParameters.JMX_VALUE;
import static net.bull.javamelody.HttpParameters.JNDI_PART;
import static net.bull.javamelody.HttpParameters.JROBINS_PART;
import static net.bull.javamelody.HttpParameters.JVM_PART;
import static net.bull.javamelody.HttpParameters.LAST_VALUE_PART;
import static net.bull.javamelody.HttpParameters.MBEANS_PART;
import static net.bull.javamelody.HttpParameters.OTHER_JROBINS_PART;
import static net.bull.javamelody.HttpParameters.PART_PARAMETER;
import static net.bull.javamelody.HttpParameters.PATH_PARAMETER;
import static net.bull.javamelody.HttpParameters.PROCESSES_PART;
import static net.bull.javamelody.HttpParameters.REQUEST_PARAMETER;
import static net.bull.javamelody.HttpParameters.SESSIONS_PART;
import static net.bull.javamelody.HttpParameters.SESSION_ID_PARAMETER;
import static net.bull.javamelody.HttpParameters.WEBAPP_VERSIONS_PART;
import static net.bull.javamelody.HttpParameters.WIDTH_PARAMETER;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
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 serveur distant.
* @author Emeric Vernat
*/
class RemoteCall {
private final URL url;
private String cookies;
/**
* Constructeur.
* @param url URL
*/
RemoteCall(URL url) {
super();
assert url != null;
this.url = url;
}
/**
* Constructeur.
* @param url String
* @throws MalformedURLException e
*/
RemoteCall(String url) throws MalformedURLException {
super();
assert url != null;
this.url = new URL(url + "?format=serialized");
}
// utilisée dans scripts Jenkins par exemple
// see https://github.com/javamelody/javamelody/wiki/ScriptsAndAlerts
JavaInformations collectJavaInformations() throws IOException {
final URL jvmUrl = new URL(url.toString() + '&' + PART_PARAMETER + '=' + JVM_PART);
final List<JavaInformations> list = collectForUrl(jvmUrl);
return list.get(0);
}
// utilisée dans scripts Jenkins par exemple
String collectMBeanAttribute(String jmxValueParameter) throws IOException {
final URL mbeanAttributeUrl = new URL(
url.toString() + '&' + JMX_VALUE + '=' + jmxValueParameter);
return collectForUrl(mbeanAttributeUrl);
}
// utilisée dans scripts Jenkins par exemple
double collectGraphLastValue(String graph) throws IOException {
final URL lastValueUrl = new URL(url.toString() + '&' + PART_PARAMETER + '='
+ LAST_VALUE_PART + '&' + GRAPH_PARAMETER + '=' + graph);
return collectForUrl(lastValueUrl);
}
// result contains statistics in instances of Counter and also an instance of JavaInformations
List<Serializable> collectData() throws IOException {
return collectForUrl(url);
}
// pourrait être utilisée dans scripts Jenkins par exemple
List<Serializable> executeActionAndCollectData(Action action, String counterName,
String sessionId, String threadId, String jobId, String cacheId) throws IOException {
assert action != null;
final URL actionUrl = getActionUrl(action, counterName, sessionId, threadId, jobId,
cacheId);
return collectForUrl(actionUrl);
}
URL getActionUrl(Action action, String counterName, String sessionId, String threadId,
String jobId, String cacheId) throws MalformedURLException {
final StringBuilder actionUrl = new StringBuilder(url.toString());
actionUrl.append("&action=").append(action);
if (counterName != null) {
actionUrl.append("&counter=").append(counterName);
}
if (sessionId != null) {
actionUrl.append("&sessionId=").append(sessionId);
}
if (threadId != null) {
actionUrl.append("&threadId=").append(threadId);
}
if (jobId != null) {
actionUrl.append("&jobId=").append(jobId);
}
if (cacheId != null) {
actionUrl.append("&cacheId=").append(cacheId);
}
return new URL(actionUrl.toString());
}
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 URL sessionsUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + SESSIONS_PART);
return collectForUrl(sessionsUrl);
}
final URL sessionsUrl = new URL(url.toString() + '&' + PART_PARAMETER + '=' + SESSIONS_PART
+ '&' + SESSION_ID_PARAMETER + '=' + sessionId);
final SessionInformations session = collectForUrl(sessionsUrl);
if (session != null) {
return Collections.singletonList(session);
}
// si session est null, alors la session a été invalidée
return Collections.emptyList();
}
List<SampledMethod> collectHotspots() throws IOException {
// récupération à la demande des hotspots
final URL hotspotsUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + HOTSPOTS_PART);
return collectForUrl(hotspotsUrl);
}
HeapHistogram collectHeapHistogram() throws IOException {
// récupération à la demande de HeapHistogram
final URL heapHistoUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + HEAP_HISTO_PART);
return collectForUrl(heapHistoUrl);
}
DatabaseInformations collectDatabaseInformations(int requestIndex) throws IOException {
final URL databaseUrl = new URL(url.toString() + '&' + PART_PARAMETER + '=' + DATABASE_PART
+ '&' + REQUEST_PARAMETER + '=' + requestIndex);
return collectForUrl(databaseUrl);
}
@SuppressWarnings("unchecked")
List<List<ConnectionInformations>> collectConnectionInformations() throws IOException {
// récupération à la demande des connections
final List<List<ConnectionInformations>> connectionInformations = new ArrayList<List<ConnectionInformations>>();
final URL connectionsUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + CONNECTIONS_PART);
final Object result = collectForUrl(connectionsUrl);
if (result instanceof List && !((List<?>) result).isEmpty()
&& ((List<?>) result).get(0) instanceof List) {
// pour le serveur de collecte
final List<List<ConnectionInformations>> connections = (List<List<ConnectionInformations>>) result;
connectionInformations.addAll(connections);
} else {
final List<ConnectionInformations> connections = (List<ConnectionInformations>) result;
connectionInformations.add(connections);
}
return connectionInformations;
}
@SuppressWarnings("unchecked")
Map<String, List<ProcessInformations>> collectProcessInformations() throws IOException {
// récupération à la demande des processus
final String title = I18N.getString("Processus");
final Map<String, List<ProcessInformations>> processesByTitle = new LinkedHashMap<String, List<ProcessInformations>>();
final URL processUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + PROCESSES_PART);
final Object result = collectForUrl(processUrl);
if (result instanceof Map) {
// pour le serveur de collecte et pour les nodes dans Jenkins
final Map<String, List<ProcessInformations>> processByTitle = (Map<String, List<ProcessInformations>>) result;
for (final Map.Entry<String, List<ProcessInformations>> entry : processByTitle
.entrySet()) {
String node = entry.getKey();
if (!node.startsWith(title)) {
// si serveur de collecte alors il y a déjà un titre, mais pas pour les nodes Jenkins
node = title + " (" + entry.getKey() + ')';
}
final List<ProcessInformations> processList = entry.getValue();
processesByTitle.put(node, processList);
}
} else {
final List<ProcessInformations> processList = (List<ProcessInformations>) result;
processesByTitle.put(title + " (" + getHostAndPort(url) + ')', processList);
}
return processesByTitle;
}
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 jndiUrl = new URL(url.toString() + '&' + PART_PARAMETER + '=' + JNDI_PART
+ (path != null ? '&' + PATH_PARAMETER + '=' + path : ""));
return collectForUrl(jndiUrl);
}
@SuppressWarnings("unchecked")
Map<String, List<MBeanNode>> collectMBeans() throws IOException {
// récupération à la demande des MBeans
final String title = I18N.getString("MBeans");
final Map<String, List<MBeanNode>> mbeansByTitle = new LinkedHashMap<String, List<MBeanNode>>();
final URL mbeansUrl = new URL(url.toString() + '&' + PART_PARAMETER + '=' + MBEANS_PART);
final Object result = collectForUrl(mbeansUrl);
if (result instanceof Map) {
// pour le serveur de collecte et les nodes dans Jenkins
final Map<String, List<MBeanNode>> mbeansByNodeName = (Map<String, List<MBeanNode>>) result;
for (final Map.Entry<String, List<MBeanNode>> entry : mbeansByNodeName.entrySet()) {
String node = entry.getKey();
if (!node.startsWith(title)) {
// si serveur de collecte alors il y a déjà un titre, mais pas pour les nodes Jenkins
node = title + " (" + entry.getKey() + ')';
}
final List<MBeanNode> mbeans = entry.getValue();
mbeansByTitle.put(node, mbeans);
}
} else {
final List<MBeanNode> mbeans = (List<MBeanNode>) result;
mbeansByTitle.put(title + " (" + getHostAndPort(url) + ')', mbeans);
}
return mbeansByTitle;
}
Map<String, MavenArtifact> collectWebappDependencies() throws IOException {
// récupération à la demande des dépendances
final URL dependenciesUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + DEPENDENCIES_PART);
return collectForUrl(dependenciesUrl);
}
Map<String, Date> collectWebappVersions() throws IOException {
final URL webappVersionsUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + WEBAPP_VERSIONS_PART);
return collectForUrl(webappVersionsUrl);
}
Map<JavaInformations, List<CounterRequestContext>> collectCurrentRequests() throws IOException {
// récupération à la demande des requêtes en cours
final URL currentRequestsUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + CURRENT_REQUESTS_PART);
return collectForUrl(currentRequestsUrl);
}
Map<String, byte[]> collectJRobins(int width, int height) throws IOException {
final URL jrobinNamesUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + JROBINS_PART + '&' + WIDTH_PARAMETER
+ '=' + width + '&' + HEIGHT_PARAMETER + '=' + height);
return collectForUrl(jrobinNamesUrl);
}
Map<String, byte[]> collectOtherJRobins(int width, int height) throws IOException {
final URL otherJRobinNamesUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + OTHER_JROBINS_PART + '&'
+ WIDTH_PARAMETER + '=' + width + '&' + HEIGHT_PARAMETER + '=' + height);
return collectForUrl(otherJRobinNamesUrl);
}
byte[] collectJRobin(String graphName, int width, int height) throws IOException {
final URL jrobinUrl = new URL(url.toString() + '&' + GRAPH_PARAMETER + '=' + graphName + '&'
+ PART_PARAMETER + '=' + JROBINS_PART + '&' + WIDTH_PARAMETER + '=' + width + '&'
+ HEIGHT_PARAMETER + '=' + height);
return collectForUrl(jrobinUrl);
}
String collectSqlRequestExplainPlan(String sqlRequest) throws IOException {
final URL explainPlanUrl = new URL(
url.toString() + '&' + PART_PARAMETER + '=' + EXPLAIN_PLAN_PART);
final Map<String, String> headers = new HashMap<String, String>();
headers.put(REQUEST_PARAMETER, sqlRequest);
if (cookies != null) {
headers.put("Cookie", cookies);
}
final LabradorRetriever labradorRetriever = new LabradorRetriever(explainPlanUrl, headers);
return labradorRetriever.call();
}
private <T> T collectForUrl(URL myUrl) throws IOException {
final LabradorRetriever labradorRetriever;
if (cookies != null) {
final Map<String, String> headers = Collections.singletonMap("Cookie", cookies);
labradorRetriever = new LabradorRetriever(myUrl, headers);
} else {
labradorRetriever = new LabradorRetriever(myUrl);
}
return labradorRetriever.call();
}
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();
}
URL getURL() {
return url;
}
void setCookies(String cookies) {
// cookies peut être null
this.cookies = cookies;
}
}