/*
* 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.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.bull.javamelody.swing.MButton;
import net.bull.javamelody.swing.Utilities;
/**
* Panel principal.
* @author Emeric Vernat
*/
class ScrollingPanel extends MelodyPanel {
static final ImageIcon PLUS_ICON = ImageIconCache.getImageIcon("bullets/plus.png");
static final ImageIcon MINUS_ICON = ImageIconCache.getImageIcon("bullets/minus.png");
private static final ImageIcon PURGE_OBSOLETE_FILES_ICON = ImageIconCache.getScaledImageIcon(
"user-trash.png", 12, 12);
private static final String DETAILS_KEY = "Details";
private static final long serialVersionUID = 1L;
private final Range range;
private final URL monitoringUrl;
private final boolean collectorServer;
private final long start = System.currentTimeMillis();
ScrollingPanel(RemoteCollector remoteCollector, Range range, URL monitoringUrl,
boolean collectorServer) {
super(remoteCollector);
this.range = range;
this.monitoringUrl = monitoringUrl;
this.collectorServer = collectorServer;
setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setOpaque(true);
setBackground(Color.decode("#E6E6E6"));
add(new ChartsPanel(remoteCollector));
addCounters();
if (!collectorServer) {
addCurrentRequests();
}
addSystemInformations();
addThreadInformations();
if (isJobEnabled()) {
addParagraphTitle(getString("Jobs"), "jobs.png");
// on ne peut utiliser collector.getRangeCounter(range, Counter.JOB_COUNTER_NAME),
// en revanche collector.getCounterByName(Counter.JOB_COUNTER_NAME) contient ici les bonnes données
final Counter rangeJobCounter = getCollector().getCounterByName(
Counter.JOB_COUNTER_NAME);
addJobs(rangeJobCounter);
addCounter(rangeJobCounter);
}
if (isCacheEnabled()) {
addParagraphTitle("Caches", "caches.png");
addCaches();
}
add(new JLabel(" "));
addDurationAndOverhead();
for (final Component component : getComponents()) {
((JComponent) component).setAlignmentX(Component.LEFT_ALIGNMENT);
}
}
private void addCounters() {
final List<Counter> counters = getCountersToBeDisplayed();
for (final Counter counter : counters) {
addCounter(counter);
}
if (range.getPeriod() == Period.TOUT && counters.size() > 1) {
final MButton clearAllCountersButton = new MButton(
getString("Reinitialiser_toutes_stats"));
clearAllCountersButton.setToolTipText(getString("Vider_toutes_stats"));
clearAllCountersButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (confirm(getString("confirm_vider_toutes_stats"))) {
actionClearAllCounters();
}
}
});
final JPanel clearAllCountersPanel = Utilities
.createButtonsPanel(clearAllCountersButton);
add(clearAllCountersPanel);
}
}
private List<Counter> getCountersToBeDisplayed() {
// on ne peut utiliser collector.getRangeCountersToBeDisplayed(range),
// en revanche collector.getCounters() contient ici les bonnes données
final List<Counter> result = new ArrayList<>(getCollector().getCounters());
final Iterator<Counter> it = result.iterator();
while (it.hasNext()) {
final Counter counter = it.next();
if (!counter.isDisplayed() || counter.isJobCounter()) {
it.remove();
}
}
return Collections.unmodifiableList(result);
}
final void actionClearAllCounters() {
try {
final String message = getRemoteCollector().executeActionAndCollectData(
Action.CLEAR_COUNTER, "all", null, null, null, null);
showMessage(message);
MainPanel.refreshMainTabFromChild(this);
} catch (final IOException ex) {
showException(ex);
}
}
private void addCounter(Counter counter) {
final String counterLabel = getString(counter.getName() + "Label");
addParagraphTitle(
getFormattedString("Statistiques_compteur", counterLabel) + " - "
+ range.getLabel(), counter.getIconName());
// pas de graphique dans les statistiques globales
final boolean includeGraph = false;
final StatisticsPanel statisticsPanel = new StatisticsPanel(getRemoteCollector(), counter,
range, includeGraph);
statisticsPanel.showGlobalRequests();
add(statisticsPanel);
}
private void addCurrentRequests() {
addParagraphTitle(getString("Requetes_en_cours"), "hourglass.png");
final Map<JavaInformations, List<CounterRequestContext>> currentRequests = getRemoteCollector()
.getCurrentRequests();
if (currentRequests.isEmpty()) {
add(new JLabel(' ' + getString("Aucune_requete_en_cours")));
} else {
for (final Map.Entry<JavaInformations, List<CounterRequestContext>> entry : currentRequests
.entrySet()) {
final JavaInformations javaInformations = entry.getKey();
final List<CounterRequestContext> contexts = entry.getValue();
final CounterRequestContextPanel firstContextPanel = new CounterRequestContextPanel(
getRemoteCollector(), contexts.subList(0, 1), javaInformations);
add(firstContextPanel);
final MButton detailsButton = new MButton(getString(DETAILS_KEY), PLUS_ICON);
final JPanel detailsPanel = firstContextPanel.createDetailsPanel(contexts,
detailsButton);
detailsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
detailsPanel.setVisible(!detailsPanel.isVisible());
detailsPanel.validate();
changePlusMinusIcon(detailsButton);
}
});
detailsPanel.setVisible(false);
add(detailsPanel);
}
}
}
private void addSystemInformations() {
addParagraphTitle(getString("Informations_systemes"), "systeminfo.png");
final List<JavaInformations> list = getJavaInformationsList();
add(new SystemInformationsButtonsPanel(getRemoteCollector(), monitoringUrl, collectorServer));
final List<JavaInformationsPanel> javaInformationsPanelList = new ArrayList<>(list.size());
final JPanel westJavaInformationsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
westJavaInformationsPanel.setOpaque(false);
for (final JavaInformations javaInformations : list) {
final JavaInformationsPanel javaInformationsPanel = new JavaInformationsPanel(
getRemoteCollector(), javaInformations, monitoringUrl);
javaInformationsPanel.showSummary();
javaInformationsPanelList.add(javaInformationsPanel);
westJavaInformationsPanel.add(javaInformationsPanel);
}
final MButton javaInformationsDetailsButton = new MButton(getString(DETAILS_KEY), PLUS_ICON);
javaInformationsDetailsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final boolean repeatHost = list.size() > 1;
for (final JavaInformationsPanel javaInformationsPanel : javaInformationsPanelList) {
javaInformationsPanel.showDetails(repeatHost);
javaInformationsPanel.validate();
}
changePlusMinusIcon(javaInformationsDetailsButton);
}
});
westJavaInformationsPanel.add(javaInformationsDetailsButton);
add(westJavaInformationsPanel);
add(new JLabel(" "));
}
private void addThreadInformations() {
addParagraphTitle(getString("Threads"), "threads.png");
for (final JavaInformations javaInformations : getJavaInformationsList()) {
final ThreadInformationsPanel threadInformationsPanel = new ThreadInformationsPanel(
getRemoteCollector(), javaInformations);
threadInformationsPanel.setVisible(false);
final JLabel summaryLabel = new JLabel("<html><b>"
+ getFormattedString("Threads_sur", javaInformations.getHost())
+ ": </b>"
+ getFormattedString("thread_count", javaInformations.getThreadCount(),
javaInformations.getPeakThreadCount(),
javaInformations.getTotalStartedThreadCount()));
final MButton detailsButton = new MButton(getString(DETAILS_KEY), PLUS_ICON);
detailsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
threadInformationsPanel.setVisible(!threadInformationsPanel.isVisible());
validate();
changePlusMinusIcon(detailsButton);
}
});
final JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
flowPanel.setOpaque(false);
flowPanel.add(summaryLabel);
flowPanel.add(detailsButton);
add(flowPanel);
for (final ThreadInformations thread : javaInformations.getThreadInformationsList()) {
if (thread.isDeadlocked()) {
// au moins un thread est en deadlock
final JLabel label = threadInformationsPanel.createThreadDeadlocksLabel();
add(label);
break;
}
}
add(threadInformationsPanel);
}
}
private void addCaches() {
for (final JavaInformations javaInformations : getJavaInformationsList()) {
if (!javaInformations.isCacheEnabled()) {
continue;
}
final List<CacheInformations> cacheInformationsList = javaInformations
.getCacheInformationsList();
final CacheInformationsPanel cacheInformationsPanel = new CacheInformationsPanel(
getRemoteCollector(), cacheInformationsList);
cacheInformationsPanel.setVisible(false);
final JLabel summaryLabel = new JLabel("<html><b>"
+ getFormattedString("caches_sur", cacheInformationsList.size(),
javaInformations.getHost()) + "</b>");
final MButton detailsButton = new MButton(getString(DETAILS_KEY), PLUS_ICON);
detailsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
cacheInformationsPanel.setVisible(!cacheInformationsPanel.isVisible());
validate();
changePlusMinusIcon(detailsButton);
}
});
final JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
flowPanel.setOpaque(false);
flowPanel.add(summaryLabel);
flowPanel.add(detailsButton);
add(flowPanel);
add(cacheInformationsPanel);
}
}
private void addJobs(Counter rangeJobCounter) {
for (final JavaInformations javaInformations : getJavaInformationsList()) {
if (!javaInformations.isJobEnabled()) {
continue;
}
final List<JobInformations> jobInformationsList = javaInformations
.getJobInformationsList();
final JobInformationsPanel jobInformationsPanel = new JobInformationsPanel(
getRemoteCollector(), jobInformationsList, rangeJobCounter);
jobInformationsPanel.setVisible(false);
final JLabel summaryLabel = new JLabel("<html><b>"
+ getFormattedString("jobs_sur", jobInformationsList.size(),
javaInformations.getHost(),
javaInformations.getCurrentlyExecutingJobCount()) + "</b>");
final MButton detailsButton = new MButton(getString(DETAILS_KEY), PLUS_ICON);
detailsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
jobInformationsPanel.setVisible(!jobInformationsPanel.isVisible());
validate();
changePlusMinusIcon(detailsButton);
}
});
final JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
flowPanel.setOpaque(false);
flowPanel.add(summaryLabel);
flowPanel.add(detailsButton);
add(flowPanel);
add(jobInformationsPanel);
}
}
private void addDurationAndOverhead() {
final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
final long displayDuration = System.currentTimeMillis() - start;
final JLabel lastCollectDurationLabel = new JLabel(getString("temps_derniere_collecte")
+ ": " + getCollector().getLastCollectDuration() + ' ' + getString("ms"));
final JLabel displayDurationLabel = new JLabel(getString("temps_affichage") + ": "
+ displayDuration + ' ' + getString("ms"));
final JLabel overheadLabel = new JLabel(getString("Estimation_overhead_memoire") + ": < "
+ (getCollector().getEstimatedMemorySize() / 1024 / 1024 + 1) + ' '
+ getString("Mo"));
lastCollectDurationLabel.setFont(lastCollectDurationLabel.getFont().deriveFont(10f));
displayDurationLabel.setFont(lastCollectDurationLabel.getFont());
overheadLabel.setFont(lastCollectDurationLabel.getFont());
panel.add(lastCollectDurationLabel);
panel.add(displayDurationLabel);
panel.add(overheadLabel);
if (Parameters.isSystemActionsEnabled()) {
final MButton purgeObsoleteFilesButton = new MButton();
purgeObsoleteFilesButton.setIcon(PURGE_OBSOLETE_FILES_ICON);
purgeObsoleteFilesButton.setToolTipText(getString("Purger_les_fichiers_obsoletes"));
purgeObsoleteFilesButton.setBorder(null);
purgeObsoleteFilesButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
purgeObsoleteFiles();
}
});
panel.add(purgeObsoleteFilesButton);
}
if (Parameters.JAVAMELODY_VERSION != null) {
panel.add(new JLabel(" "));
final JLabel versionLabel = new JLabel("JavaMelody " + Parameters.JAVAMELODY_VERSION);
versionLabel.setFont(lastCollectDurationLabel.getFont());
panel.add(versionLabel);
}
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
panel.setOpaque(false);
add(panel);
}
final void purgeObsoleteFiles() {
try {
final String message = getRemoteCollector().executeActionAndCollectData(
Action.PURGE_OBSOLETE_FILES, null, null, null, null, null);
showMessage(message);
MainPanel.refreshMainTabFromChild(this);
} catch (final IOException ex) {
showException(ex);
}
}
final void changePlusMinusIcon(MButton detailsButton) {
if (detailsButton.getIcon() == PLUS_ICON) {
detailsButton.setIcon(MINUS_ICON);
} else {
detailsButton.setIcon(PLUS_ICON);
}
}
private boolean isCacheEnabled() {
for (final JavaInformations javaInformations : getJavaInformationsList()) {
if (javaInformations.isCacheEnabled()) {
return true;
}
}
return false;
}
private boolean isJobEnabled() {
for (final JavaInformations javaInformations : getJavaInformationsList()) {
if (javaInformations.isJobEnabled()) {
return true;
}
}
return false;
}
private void addParagraphTitle(String title, String iconName) {
final JLabel label = Utilities.createParagraphTitle(title, iconName);
add(label);
}
}