/*
* 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.CACHE_ID_PARAMETER;
import static net.bull.javamelody.HttpParameters.CACHE_KEYS_PART;
import static net.bull.javamelody.HttpParameters.CLASS_PARAMETER;
import static net.bull.javamelody.HttpParameters.CONNECTIONS_PART;
import static net.bull.javamelody.HttpParameters.COUNTER_PARAMETER;
import static net.bull.javamelody.HttpParameters.COUNTER_SUMMARY_PER_CLASS_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.FORMAT_PARAMETER;
import static net.bull.javamelody.HttpParameters.GRAPH_PARAMETER;
import static net.bull.javamelody.HttpParameters.GRAPH_PART;
import static net.bull.javamelody.HttpParameters.HEAP_HISTO_PART;
import static net.bull.javamelody.HttpParameters.HOTSPOTS_PART;
import static net.bull.javamelody.HttpParameters.HTML_BODY_FORMAT;
import static net.bull.javamelody.HttpParameters.HTML_CHARSET;
import static net.bull.javamelody.HttpParameters.HTML_CONTENT_TYPE;
import static net.bull.javamelody.HttpParameters.JNDI_PART;
import static net.bull.javamelody.HttpParameters.MBEANS_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.SOURCE_PART;
import static net.bull.javamelody.HttpParameters.SPRING_BEANS_PART;
import static net.bull.javamelody.HttpParameters.TEXT_CONTENT_TYPE;
import static net.bull.javamelody.HttpParameters.THREADS_DUMP_PART;
import static net.bull.javamelody.HttpParameters.THREADS_PART;
import static net.bull.javamelody.HttpParameters.USAGES_PART;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.bull.javamelody.SamplingProfiler.SampledMethod;
/**
* Contrôleur au sens MVC de l'ihm de monitoring pour la partie html.
* @author Emeric Vernat
*/
class HtmlController {
private final HttpCookieManager httpCookieManager = new HttpCookieManager();
private final Collector collector;
private final CollectorServer collectorServer;
private final String messageForReport;
private final String anchorNameForRedirect;
HtmlController(Collector collector, CollectorServer collectorServer, String messageForReport,
String anchorNameForRedirect) {
super();
assert collector != null;
this.collector = collector;
this.collectorServer = collectorServer;
this.messageForReport = messageForReport;
this.anchorNameForRedirect = anchorNameForRedirect;
}
void doHtml(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
List<JavaInformations> javaInformationsList) throws IOException {
final String part = httpRequest.getParameter(PART_PARAMETER);
if (!isFromCollectorServer() && isLocalCollectNeeded(part) && !collector.isStopped()) {
// avant de faire l'affichage on fait une collecte, pour que les courbes
// et les compteurs par jour soit à jour avec les dernières requêtes,
// sauf si c'est un serveur de collecte
// ou si la page de monitoring d'une webapp monitorée par un serveur de collecte est appelée par erreur
collector.collectLocalContextWithoutErrors();
}
// simple appel de monitoring sans format
httpResponse.setContentType(HTML_CONTENT_TYPE);
final BufferedWriter writer = getWriter(httpResponse);
try {
final Range range = httpCookieManager.getRange(httpRequest, httpResponse);
final HtmlReport htmlReport = new HtmlReport(collector, collectorServer,
javaInformationsList, range, writer);
if (part == null) {
htmlReport.toHtml(messageForReport, anchorNameForRedirect);
} else if (THREADS_DUMP_PART.equalsIgnoreCase(part)) {
httpResponse.setContentType(TEXT_CONTENT_TYPE);
htmlReport.writeThreadsDump();
} else {
doHtmlPart(httpRequest, part, htmlReport);
}
} finally {
writer.close();
}
}
static boolean isLocalCollectNeeded(final String part) {
return part == null || CURRENT_REQUESTS_PART.equalsIgnoreCase(part)
|| GRAPH_PART.equalsIgnoreCase(part)
|| COUNTER_SUMMARY_PER_CLASS_PART.equalsIgnoreCase(part);
}
static BufferedWriter getWriter(HttpServletResponse httpResponse) throws IOException {
return new BufferedWriter(
new OutputStreamWriter(httpResponse.getOutputStream(), HTML_CHARSET));
}
private void doHtmlPart(HttpServletRequest httpRequest, String part, HtmlReport htmlReport)
throws IOException {
if (GRAPH_PART.equalsIgnoreCase(part)) {
final String graphName = httpRequest.getParameter(GRAPH_PARAMETER);
htmlReport.writeRequestAndGraphDetail(graphName);
} else if (USAGES_PART.equalsIgnoreCase(part)) {
final String graphName = httpRequest.getParameter(GRAPH_PARAMETER);
htmlReport.writeRequestUsages(graphName);
} else if (CURRENT_REQUESTS_PART.equalsIgnoreCase(part)) {
htmlReport.writeAllCurrentRequestsAsPart();
} else if (THREADS_PART.equalsIgnoreCase(part)) {
htmlReport.writeAllThreadsAsPart();
} else if (COUNTER_SUMMARY_PER_CLASS_PART.equalsIgnoreCase(part)) {
final String counterName = httpRequest.getParameter(COUNTER_PARAMETER);
final String requestId = httpRequest.getParameter(GRAPH_PARAMETER);
htmlReport.writeCounterSummaryPerClass(counterName, requestId);
} else if (SOURCE_PART.equalsIgnoreCase(part)) {
final String className = httpRequest.getParameter(CLASS_PARAMETER);
htmlReport.writeSource(className);
} else if (DEPENDENCIES_PART.equalsIgnoreCase(part)) {
htmlReport.writeDependencies();
} else if (CACHE_KEYS_PART.equalsIgnoreCase(part)) {
final boolean withoutHeaders = HTML_BODY_FORMAT
.equalsIgnoreCase(httpRequest.getParameter(FORMAT_PARAMETER));
doCacheKeys(htmlReport, httpRequest.getParameter(CACHE_ID_PARAMETER), withoutHeaders);
} else {
doHtmlPartForSystemActions(httpRequest, part, htmlReport);
}
}
private void doHtmlPartForSystemActions(HttpServletRequest httpRequest, String part,
HtmlReport htmlReport) throws IOException {
if (SESSIONS_PART.equalsIgnoreCase(part)) {
doSessions(htmlReport, httpRequest.getParameter(SESSION_ID_PARAMETER));
} else if (HOTSPOTS_PART.equalsIgnoreCase(part)) {
doHotspots(htmlReport);
} else if (HEAP_HISTO_PART.equalsIgnoreCase(part)) {
doHeapHisto(htmlReport);
} else if (PROCESSES_PART.equalsIgnoreCase(part)) {
doProcesses(htmlReport);
} else if (DATABASE_PART.equalsIgnoreCase(part)) {
final int requestIndex = DatabaseInformations
.parseRequestIndex(httpRequest.getParameter(REQUEST_PARAMETER));
doDatabase(htmlReport, requestIndex);
} else if (CONNECTIONS_PART.equalsIgnoreCase(part)) {
final boolean withoutHeaders = HTML_BODY_FORMAT
.equalsIgnoreCase(httpRequest.getParameter(FORMAT_PARAMETER));
doConnections(htmlReport, withoutHeaders);
} else if (JNDI_PART.equalsIgnoreCase(part)) {
doJndi(htmlReport, httpRequest.getParameter(PATH_PARAMETER));
} else if (MBEANS_PART.equalsIgnoreCase(part)) {
doMBeans(htmlReport);
} else if (SPRING_BEANS_PART.equalsIgnoreCase(part)) {
htmlReport.writeSpringContext();
} else {
throw new IllegalArgumentException(part);
}
}
private void doSessions(HtmlReport htmlReport, String sessionId) throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
final List<SessionInformations> sessionsInformations;
if (!isFromCollectorServer()) {
if (sessionId == null) {
sessionsInformations = SessionListener.getAllSessionsInformations();
} else {
sessionsInformations = Collections.singletonList(
SessionListener.getSessionInformationsBySessionId(sessionId));
}
} else {
sessionsInformations = collectorServer.collectSessionInformations(getApplication(),
sessionId);
}
if (sessionId == null || sessionsInformations.isEmpty()) {
htmlReport.writeSessions(sessionsInformations, messageForReport, SESSIONS_PART);
} else {
final SessionInformations sessionInformation = sessionsInformations.get(0);
htmlReport.writeSessionDetail(sessionId, sessionInformation);
}
}
private void doHotspots(HtmlReport htmlReport) throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
if (!isFromCollectorServer()) {
final List<SampledMethod> hotspots = collector.getHotspots();
htmlReport.writeHotspots(hotspots);
} else {
final List<SampledMethod> hotspots = collectorServer.collectHotspots(getApplication());
htmlReport.writeHotspots(hotspots);
}
}
private void doHeapHisto(HtmlReport htmlReport) throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
final HeapHistogram heapHistogram;
try {
if (!isFromCollectorServer()) {
heapHistogram = VirtualMachine.createHeapHistogram();
} else {
heapHistogram = collectorServer.collectHeapHistogram(getApplication());
}
} catch (final Exception e) {
LOG.warn("heaphisto report failed", e);
htmlReport.writeMessageIfNotNull(String.valueOf(e.getMessage()), null);
return;
}
htmlReport.writeHeapHistogram(heapHistogram, messageForReport, HEAP_HISTO_PART);
}
private void doProcesses(HtmlReport htmlReport) throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
try {
if (!isFromCollectorServer()) {
final List<ProcessInformations> processInformationsList = ProcessInformations
.buildProcessInformations();
htmlReport.writeProcesses(processInformationsList);
} else {
final Map<String, List<ProcessInformations>> processInformationsByTitle = collectorServer
.collectProcessInformations(getApplication());
htmlReport.writeProcesses(processInformationsByTitle);
}
} catch (final Exception e) {
LOG.warn("processes report failed", e);
htmlReport.writeMessageIfNotNull(String.valueOf(e.getMessage()), null);
}
}
private void doDatabase(HtmlReport htmlReport, int index) throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
try {
final DatabaseInformations databaseInformations;
if (!isFromCollectorServer()) {
databaseInformations = new DatabaseInformations(index);
} else {
databaseInformations = collectorServer.collectDatabaseInformations(getApplication(),
index);
}
htmlReport.writeDatabase(databaseInformations);
} catch (final Exception e) {
LOG.warn("database report failed", e);
htmlReport.writeMessageIfNotNull(String.valueOf(e.getMessage()), null);
}
}
private void doConnections(HtmlReport htmlReport, boolean withoutHeaders) throws IOException {
assert !isFromCollectorServer();
// par sécurité
Action.checkSystemActionsEnabled();
htmlReport.writeConnections(JdbcWrapper.getConnectionInformationsList(), withoutHeaders);
}
private void doJndi(HtmlReport htmlReport, String path) throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
try {
final List<JndiBinding> jndiBindings;
if (!isFromCollectorServer()) {
jndiBindings = JndiBinding.listBindings(path);
} else {
jndiBindings = collectorServer.collectJndiBindings(getApplication(), path);
}
htmlReport.writeJndi(jndiBindings, path);
} catch (final Exception e) {
LOG.warn("jndi report failed", e);
htmlReport.writeMessageIfNotNull(String.valueOf(e.getMessage()), null);
}
}
private void doMBeans(HtmlReport htmlReport) throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
try {
if (!isFromCollectorServer()) {
final List<MBeanNode> nodes = MBeans.getAllMBeanNodes();
htmlReport.writeMBeans(nodes);
} else {
final Map<String, List<MBeanNode>> allMBeans = collectorServer
.collectMBeans(getApplication());
htmlReport.writeMBeans(allMBeans);
}
} catch (final Exception e) {
LOG.warn("mbeans report failed", e);
htmlReport.writeMessageIfNotNull(String.valueOf(e.getMessage()), null);
}
}
private void doCacheKeys(HtmlReport htmlReport, String cacheId, boolean withoutHeaders)
throws IOException {
assert !isFromCollectorServer();
final CacheInformations cacheInfo = CacheInformations
.buildCacheInformationsWithKeys(cacheId);
htmlReport.writeCacheWithKeys(cacheId, cacheInfo, messageForReport,
CACHE_KEYS_PART + '&' + CACHE_ID_PARAMETER + '=' + I18N.urlEncode(cacheId),
withoutHeaders);
}
void writeHtmlToLastShutdownFile() {
try {
final File dir = Parameters.getStorageDirectory(getApplication());
if (!dir.mkdirs() && !dir.exists()) {
throw new IOException("JavaMelody directory can't be created: " + dir.getPath());
}
final File lastShutdownFile = new File(dir, "last_shutdown.html");
final BufferedWriter writer = new BufferedWriter(new FileWriter(lastShutdownFile));
try {
final JavaInformations javaInformations = new JavaInformations(
Parameters.getServletContext(), true);
// on pourrait faire I18N.bindLocale(Locale.getDefault()), mais cela se fera tout seul
final HtmlReport htmlReport = new HtmlReport(collector, collectorServer,
Collections.singletonList(javaInformations), Period.JOUR, writer);
htmlReport.writeLastShutdown();
} finally {
writer.close();
}
} catch (final IOException e) {
LOG.warn("exception while writing the last shutdown report", e);
}
}
private String getApplication() {
return collector.getApplication();
}
private boolean isFromCollectorServer() {
return collectorServer != null;
}
}