net.bull.javamelody.internal.web.SerializableController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javamelody-core Show documentation
Show all versions of javamelody-core Show documentation
Monitoring of JavaEE applications
/*
* Copyright 2008-2019 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.internal.web; // NOPMD
import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import javax.management.JMException;
import javax.naming.NamingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.bull.javamelody.JdbcWrapper;
import net.bull.javamelody.SessionListener;
import net.bull.javamelody.internal.common.HttpParameter;
import net.bull.javamelody.internal.common.HttpPart;
import net.bull.javamelody.internal.common.I18N;
import net.bull.javamelody.internal.model.Action;
import net.bull.javamelody.internal.model.Collector;
import net.bull.javamelody.internal.model.Counter;
import net.bull.javamelody.internal.model.CounterRequest;
import net.bull.javamelody.internal.model.CounterRequestAggregation;
import net.bull.javamelody.internal.model.CounterRequestContext;
import net.bull.javamelody.internal.model.DatabaseInformations;
import net.bull.javamelody.internal.model.HsErrPid;
import net.bull.javamelody.internal.model.JRobin;
import net.bull.javamelody.internal.model.JavaInformations;
import net.bull.javamelody.internal.model.JndiBinding;
import net.bull.javamelody.internal.model.MBeans;
import net.bull.javamelody.internal.model.MavenArtifact;
import net.bull.javamelody.internal.model.Period;
import net.bull.javamelody.internal.model.ProcessInformations;
import net.bull.javamelody.internal.model.Range;
import net.bull.javamelody.internal.model.TransportFormat;
import net.bull.javamelody.internal.model.VirtualMachine;
import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestAttribute;
import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestHeader;
import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestParameter;
import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestPart;
/**
* Contrôleur au sens MVC pour la partie des données sérialisées.
* @author Emeric Vernat
*/
public class SerializableController {
private static final String RANGE_KEY = "range";
private static final String JAVA_INFORMATIONS_LIST_KEY = "javaInformationsList";
private static final String MESSAGE_FOR_REPORT_KEY = "messageForReport";
private static final RequestToMethodMapper REQUEST_TO_METHOD_MAPPER = new RequestToMethodMapper<>(
SerializableController.class);
private final Collector collector;
public SerializableController(Collector collector) {
super();
assert collector != null;
this.collector = collector;
}
void doSerializable(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
Serializable serializable) throws IOException {
// l'appelant (un serveur d'agrégation par exemple) peut appeler
// la page monitoring avec un format "serialized" ou "xml" en paramètre
// pour avoir les données au format sérialisé java ou xml
final String format = HttpParameter.FORMAT.getParameterFrom(httpRequest);
final TransportFormat transportFormat = TransportFormat.valueOfIgnoreCase(format);
// checkDependencies avant setContentType pour afficher correctement les erreurs
transportFormat.checkDependencies();
httpResponse.setContentType(transportFormat.getMimeType());
final String fileName = "JavaMelody_" + getApplication().replace(' ', '_').replace("/", "")
+ '_' + I18N.getCurrentDate().replace('/', '_') + '.' + transportFormat.getCode();
final String contentDisposition = "inline;filename=" + fileName;
// encoding des CRLF pour http://en.wikipedia.org/wiki/HTTP_response_splitting
httpResponse.addHeader("Content-Disposition",
contentDisposition.replace('\n', '_').replace('\r', '_'));
transportFormat.writeSerializableTo(serializable, httpResponse.getOutputStream());
}
public Serializable createSerializable(HttpServletRequest httpRequest,
List javaInformationsList, String messageForReport)
throws IOException {
final Range range = getRangeForSerializable(httpRequest);
if (HttpParameter.PART.getParameterFrom(httpRequest) != null) {
httpRequest.setAttribute(JAVA_INFORMATIONS_LIST_KEY, javaInformationsList);
httpRequest.setAttribute(RANGE_KEY, range);
httpRequest.setAttribute(MESSAGE_FOR_REPORT_KEY, messageForReport);
return (Serializable) REQUEST_TO_METHOD_MAPPER.invokeAndReturn(httpRequest, this);
} else if (HttpParameter.JMX_VALUE.getParameterFrom(httpRequest) != null) {
// par sécurité
Action.checkSystemActionsEnabled();
final String jmxValue = HttpParameter.JMX_VALUE.getParameterFrom(httpRequest);
return MBeans.getConvertedAttributes(jmxValue);
}
return createDefaultSerializable(javaInformationsList, range, messageForReport);
}
@RequestPart(HttpPart.THREADS)
Serializable createThreadsSerializable(
@RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List javaInformationsList) {
return new ArrayList<>(javaInformationsList.get(0).getThreadInformationsList());
}
@RequestPart(HttpPart.COUNTER_SUMMARY_PER_CLASS)
Serializable createCounterSummaryPerClassSerializable(@RequestAttribute(RANGE_KEY) Range range,
@RequestParameter(HttpParameter.COUNTER) String counterName,
@RequestParameter(HttpParameter.GRAPH) String requestId) throws IOException {
final Counter counter = collector.getRangeCounter(range, counterName).clone();
final List requestList = new CounterRequestAggregation(counter)
.getRequestsAggregatedOrFilteredByClassName(requestId);
return new ArrayList<>(requestList);
}
@RequestPart(HttpPart.CURRENT_REQUESTS)
Serializable createCurrentRequestsSerializable(
@RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List javaInformationsList) {
final Map> result = new HashMap<>();
result.put(javaInformationsList.get(0), getCurrentRequests());
return (Serializable) result;
}
@RequestPart(HttpPart.DEFAULT_WITH_CURRENT_REQUESTS)
@SuppressWarnings("unchecked")
Serializable createDefaultWithCurrentRequestsSerializable(
@RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List javaInformationsList,
@RequestAttribute(MESSAGE_FOR_REPORT_KEY) String messageForReport,
@RequestAttribute(RANGE_KEY) Range range) throws IOException {
final List result = new ArrayList<>(
(List) createDefaultSerializable(javaInformationsList, range,
messageForReport));
result.addAll(getCurrentRequests());
return (Serializable) result;
}
@RequestPart(HttpPart.JVM)
Serializable createJvmSerializable(
@RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List javaInformationsList) {
return new ArrayList<>(javaInformationsList);
}
@RequestPart(HttpPart.SESSIONS)
Serializable createSessionsSerializable(
@RequestParameter(HttpParameter.SESSION_ID) String sessionId) {
// par sécurité
Action.checkSystemActionsEnabled();
if (sessionId == null) {
return new ArrayList<>(SessionListener.getAllSessionsInformations());
}
return SessionListener.getSessionInformationsBySessionId(sessionId);
}
@RequestPart(HttpPart.HOTSPOTS)
Serializable createHotspotsSerializable() {
// par sécurité
Action.checkSystemActionsEnabled();
return new ArrayList<>(collector.getHotspots());
}
@RequestPart(HttpPart.HEAP_HISTO)
Serializable createHeapHistoSerializable() throws Exception { // NOPMD
// par sécurité
Action.checkSystemActionsEnabled();
return VirtualMachine.createHeapHistogram();
}
@RequestPart(HttpPart.PROCESSES)
Serializable createProcessesSerializable() throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
return new ArrayList<>(ProcessInformations.buildProcessInformations());
}
@RequestPart(HttpPart.JNDI)
Serializable createJndiSerializable(@RequestParameter(HttpParameter.PATH) String path)
throws NamingException {
// par sécurité
Action.checkSystemActionsEnabled();
return new ArrayList<>(JndiBinding.listBindings(path));
}
@RequestPart(HttpPart.MBEANS)
Serializable createMBeansSerializable() throws JMException {
// par sécurité
Action.checkSystemActionsEnabled();
return new ArrayList<>(MBeans.getAllMBeanNodes());
}
@RequestPart(HttpPart.DEPENDENCIES)
Serializable createDependenciesSerializable() throws IOException {
// par sécurité
Action.checkSystemActionsEnabled();
final Map webappDependencies = MavenArtifact.getWebappDependencies();
for (final MavenArtifact dependency : webappDependencies.values()) {
if (dependency != null) {
// preload licenses with parent of dependency when needed
dependency.getLicenseUrlsByName();
}
}
return new TreeMap<>(webappDependencies);
}
@RequestPart(HttpPart.LAST_VALUE)
Serializable createLastValueSerializable(@RequestParameter(HttpParameter.GRAPH) String graph)
throws IOException {
if (graph != null) {
final JRobin jrobin = collector.getJRobin(graph);
final double lastValue;
if (jrobin == null) {
lastValue = -1;
} else {
lastValue = jrobin.getLastValue();
}
return lastValue;
}
final Collection jrobins = collector.getDisplayedCounterJRobins();
final Map lastValues = new LinkedHashMap<>(jrobins.size());
for (final JRobin jrobin : jrobins) {
lastValues.put(jrobin.getName(), jrobin.getLastValue());
}
return (Serializable) lastValues;
}
@RequestPart(HttpPart.DATABASE)
Serializable createDatabaseSerializable(
@RequestParameter(HttpParameter.REQUEST) String requestIndex)
throws SQLException, NamingException {
// par sécurité
Action.checkSystemActionsEnabled();
final int index = DatabaseInformations.parseRequestIndex(requestIndex);
return new DatabaseInformations(index);
}
@RequestPart(HttpPart.CONNECTIONS)
Serializable createConnectionsSerializable() {
// par sécurité
Action.checkSystemActionsEnabled();
return new ArrayList<>(JdbcWrapper.getConnectionInformationsList());
}
@RequestPart(HttpPart.WEBAPP_VERSIONS)
Serializable createWebappVersionsSerializable() {
return new LinkedHashMap<>(collector.getDatesByWebappVersions());
}
@RequestPart(HttpPart.GRAPH)
Serializable getCounterRequestById(@RequestParameter(HttpParameter.GRAPH) String requestId,
@RequestAttribute(RANGE_KEY) Range range) throws IOException {
for (final Counter counter : collector.getCounters()) {
if (counter.isRequestIdFromThisCounter(requestId)) {
final Counter rangeCounter = collector.getRangeCounter(range, counter.getName())
.clone();
for (final CounterRequest request : rangeCounter.getRequests()) {
if (requestId.equals(request.getId())) {
return request;
}
}
}
}
// non trouvé
return null;
}
@RequestPart(HttpPart.JROBINS)
Serializable getJRobinsImages(@RequestAttribute(RANGE_KEY) Range range,
@RequestParameter(HttpParameter.WIDTH) String width,
@RequestParameter(HttpParameter.HEIGHT) String height,
@RequestParameter(HttpParameter.GRAPH) String graphName) throws IOException {
// pour UI Swing
final int myWidth = Integer.parseInt(width);
final int myHeight = Integer.parseInt(height);
if (graphName != null) {
final JRobin jrobin = collector.getJRobin(graphName);
if (jrobin != null) {
return jrobin.graph(range, myWidth, myHeight);
}
return null;
}
final Collection jrobins = collector.getDisplayedCounterJRobins();
return (Serializable) convertJRobinsToImages(jrobins, range, myWidth, myHeight);
}
@RequestPart(HttpPart.OTHER_JROBINS)
Serializable getOtherJRobinsImages(@RequestAttribute(RANGE_KEY) Range range,
@RequestParameter(HttpParameter.WIDTH) String width,
@RequestParameter(HttpParameter.HEIGHT) String height) throws IOException {
// pour UI Swing
final Collection jrobins = collector.getDisplayedOtherJRobins();
return (Serializable) convertJRobinsToImages(jrobins, range, Integer.parseInt(width),
Integer.parseInt(height));
}
@RequestPart(HttpPart.EXPLAIN_PLAN)
Serializable createExplainPlanSerializableFor(@RequestHeader("request") String sqlRequest) {
// pour UI Swing
assert sqlRequest != null;
try {
// retourne le plan d'exécution ou null si la base de données ne le permet pas (ie non Oracle)
return DatabaseInformations.explainPlanFor(sqlRequest);
} catch (final Exception ex) {
return ex.toString();
}
}
private List getCurrentRequests() {
final List counters = collector.getCounters();
final List newCounters = new ArrayList<>();
for (final Counter counter : counters) {
final Counter cloneLight = new Counter(counter.getName(), counter.getStorageName(),
counter.getIconName(), counter.getChildCounterName());
newCounters.add(cloneLight);
}
// note: ces contextes ont été clonés dans getRootCurrentContexts(newCounters) par getOrderedRootCurrentContexts()
return collector.getRootCurrentContexts(newCounters);
}
@RequestPart(HttpPart.CRASHES)
Serializable createCrashesSerializable(
@RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List javaInformationsList) {
return new ArrayList<>(HsErrPid.getHsErrPidList(javaInformationsList));
}
private Map convertJRobinsToImages(Collection jrobins, Range range,
int width, int height) throws IOException {
final Map images = new LinkedHashMap<>(jrobins.size());
for (final JRobin jrobin : jrobins) {
final byte[] image = jrobin.graph(range, width, height);
images.put(jrobin.getName(), image);
}
return images;
}
public Serializable createDefaultSerializable(List javaInformationsList,
Range range, String messageForReport) throws IOException {
final List counters = collector.getRangeCounters(range);
final List serialized = new ArrayList<>(
counters.size() + javaInformationsList.size());
// on clone les counters avant de les sérialiser pour ne pas avoir de problèmes de concurrences d'accès
for (final Counter counter : counters) {
serialized.add(counter.clone());
}
serialized.addAll(javaInformationsList);
if (messageForReport != null) {
serialized.add(messageForReport);
}
return (Serializable) serialized;
}
public Range getRangeForSerializable(HttpServletRequest httpRequest) {
final Range range;
final String period = HttpParameter.PERIOD.getParameterFrom(httpRequest);
if (period == null) {
// période tout par défaut pour Serializable, notamment pour le serveur de collecte
range = Period.TOUT.getRange();
} else {
final DateFormat dateFormat;
final String pattern = HttpParameter.PATTERN.getParameterFrom(httpRequest);
if (pattern == null) {
dateFormat = I18N.createDateFormat();
} else {
dateFormat = new SimpleDateFormat(pattern, Locale.US);
}
range = Range.parse(period, dateFormat);
}
return range;
}
private String getApplication() {
return collector.getApplication();
}
}