/*
* 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.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import net.bull.javamelody.swing.MButton;
import net.bull.javamelody.swing.Utilities;
import net.bull.javamelody.swing.table.MDateTableCellRenderer;
import net.bull.javamelody.swing.table.MDefaultTableCellRenderer;
import net.bull.javamelody.swing.table.MTable;
import net.bull.javamelody.swing.table.MTableScrollPane;
/**
* Panel de la liste des connexions jdbc.
* @author Emeric Vernat
*/
class ConnectionInformationsPanel extends MelodyPanel {
private static final long serialVersionUID = 1L;
@SuppressWarnings("all")
private List<ConnectionInformations> connectionInformationsList;
private boolean stackTraceEnabled;
@SuppressWarnings("all")
private Map<ConnectionInformations, ThreadInformations> threadInformationsByConnectionInformations;
private MTable<ConnectionInformations> table;
private class OpeningDateTableCellRenderer extends MDateTableCellRenderer {
private static final long serialVersionUID = 1L;
OpeningDateTableCellRenderer() {
super();
setDateFormat(I18N.createDateAndTimeFormat());
}
@Override
public Component getTableCellRendererComponent(JTable jtable, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
// tooltip selon la stack-trace
if (row == -1) {
setToolTipText(null);
} else {
final MTable<ConnectionInformations> myTable = getTable();
final ConnectionInformations connectionInformations = myTable.getList().get(
myTable.convertRowIndexToModel(row));
final String description = getDateFormat().format(
connectionInformations.getOpeningDate());
final List<StackTraceElement> stackTrace = connectionInformations
.getOpeningStackTrace();
setToolTipText(ThreadInformationsPanel.convertStackTraceToHtml(description,
stackTrace));
}
// et texte selon la valeur (nom du thread)
return super.getTableCellRendererComponent(jtable, value, isSelected, hasFocus, row,
column);
}
}
private class ThreadTableCellRenderer extends MDefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
ThreadTableCellRenderer() {
super();
}
@Override
public Component getTableCellRendererComponent(JTable jtable, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
// tooltip selon la stack-trace
final String text;
if (row == -1) {
setToolTipText(null);
text = null;
} else {
final MTable<ConnectionInformations> myTable = getTable();
final ConnectionInformations connectionInformations = myTable.getList().get(
myTable.convertRowIndexToModel(row));
final ThreadInformations threadInformations = getThreadInformationsByConnectionInformations(connectionInformations);
if (threadInformations != null) {
text = threadInformations.getName();
setToolTipText(ThreadInformationsPanel.convertStackTraceToHtml(
threadInformations.getName(), threadInformations.getStackTrace()));
} else {
setToolTipText(null);
text = null;
}
}
// et texte selon la valeur (nom du thread)
return super.getTableCellRendererComponent(jtable, text, isSelected, hasFocus, row,
column);
}
}
ConnectionInformationsPanel(RemoteCollector remoteCollector) throws IOException {
super(remoteCollector);
refresh();
}
final void refresh() throws IOException {
removeAll();
final List<List<ConnectionInformations>> allConnectionInformations = getRemoteCollector()
.collectConnectionInformations();
// collecte aussi les data pour avoir des stack-traces de threads à jour
getRemoteCollector().collectDataIncludingCurrentRequests();
final List<JavaInformations> javaInformationsList = getJavaInformationsList();
for (final JavaInformations javaInformations : javaInformationsList) {
if (javaInformations.isStackTraceEnabled()) {
stackTraceEnabled = true;
break;
}
}
// on mélange toutes les connexions des éventuels différents serveurs ensemble mais c'est pas bien grave
this.connectionInformationsList = new ArrayList<>();
this.threadInformationsByConnectionInformations = new HashMap<>();
int i = 0;
for (final List<ConnectionInformations> connections : allConnectionInformations) {
connectionInformationsList.addAll(connections);
// on recherche les infos courantes sur le thread ayant ouvert la connexion
// (attention : l'id du thread n'est pas forcément unique s'il y a plusieurs JVM)
final JavaInformations javaInformations = javaInformationsList.get(i);
for (final ConnectionInformations connectionInformations : connections) {
for (final ThreadInformations threadInformations : javaInformations
.getThreadInformationsList()) {
if (connectionInformations.getThreadId() == threadInformations.getId()) {
threadInformationsByConnectionInformations.put(connectionInformations,
threadInformations);
}
}
}
i++;
}
setName(getString("Connexions_jdbc_ouvertes"));
final JLabel titleLabel = Utilities.createParagraphTitle(getName(), "db.png");
add(titleLabel, BorderLayout.NORTH);
final JLabel introLabel = new JLabel(' ' + getString("connexions_intro"));
final MTableScrollPane<ConnectionInformations> scrollPane = createScrollPane();
this.table = scrollPane.getTable();
table.setList(connectionInformationsList);
final JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.setOpaque(false);
centerPanel.add(introLabel, BorderLayout.NORTH);
centerPanel.add(scrollPane, BorderLayout.CENTER);
add(centerPanel, BorderLayout.CENTER);
final JLabel nbConnectionsLabel = new JLabel(getFormattedString("nb_connexions_ouvertes",
connectionInformationsList.size()));
nbConnectionsLabel.setHorizontalAlignment(SwingConstants.RIGHT);
final JPanel southPanel = new JPanel(new BorderLayout());
southPanel.setOpaque(false);
southPanel.add(createButtonsPanel(), BorderLayout.NORTH);
southPanel.add(nbConnectionsLabel, BorderLayout.CENTER);
add(southPanel, BorderLayout.SOUTH);
}
private MTableScrollPane<ConnectionInformations> createScrollPane() {
final MTableScrollPane<ConnectionInformations> tableScrollPane = new MTableScrollPane<>();
final MTable<ConnectionInformations> myTable = tableScrollPane.getTable();
myTable.addColumn("openingDate", getString("Date_et_stack_trace_ouverture"));
if (stackTraceEnabled) {
myTable.addColumn("threadId", getString("Thread_et_stack_trace_actuelle"));
} else {
myTable.addColumn("threadId", getString("Thread"));
}
myTable.setColumnCellRenderer("openingDate", new OpeningDateTableCellRenderer());
myTable.setColumnCellRenderer("threadId", new ThreadTableCellRenderer());
return tableScrollPane;
}
final void showStackTraceInPopup(ConnectionInformations connectionInformations,
ThreadInformations threadInformations) {
final StringBuilder sb = new StringBuilder();
sb.append(getString("Date_et_stack_trace_ouverture"));
sb.append(": ");
sb.append(I18N.createDateAndTimeFormat().format(connectionInformations.getOpeningDate()));
sb.append('\n');
for (final StackTraceElement stackTraceElement : connectionInformations
.getOpeningStackTrace()) {
sb.append(stackTraceElement);
sb.append('\n');
}
sb.append('\n');
if (threadInformations != null && stackTraceEnabled) {
sb.append(getString("Thread_et_stack_trace_actuelle"));
sb.append(": ");
final List<StackTraceElement> stackTrace = threadInformations.getStackTrace();
if (stackTrace != null && !stackTrace.isEmpty()) {
// même si stackTraceEnabled, ce thread n'a pas forcément de stack-trace
sb.append(threadInformations.getName());
sb.append('\n');
for (final StackTraceElement stackTraceElement : stackTrace) {
sb.append(stackTraceElement);
sb.append('\n');
}
final String title = threadInformations.getName();
final String text = sb.toString();
Utilities.showTextInPopup(this, title, text);
}
}
}
private JPanel createButtonsPanel() {
final MButton openButton = new MButton(getString("Ouvrir"),
ImageIconCache.getImageIcon("action_open.png"));
final MButton refreshButton = createRefreshButton();
openButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final ConnectionInformations connectionInformations = getTable()
.getSelectedObject();
final ThreadInformations threadInformations = getThreadInformationsByConnectionInformations(connectionInformations);
showStackTraceInPopup(connectionInformations, threadInformations);
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
openButton.doClick();
}
}
});
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
final ConnectionInformations connectionInformations = getTable()
.getSelectedObject();
openButton.setEnabled(connectionInformations != null);
}
});
openButton.setEnabled(false);
refreshButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
refresh();
} catch (final IOException ex) {
showException(ex);
}
}
});
return Utilities.createButtonsPanel(openButton, refreshButton);
}
final MTable<ConnectionInformations> getTable() {
return table;
}
final ThreadInformations getThreadInformationsByConnectionInformations(
ConnectionInformations connectionInformations) {
return threadInformationsByConnectionInformations.get(connectionInformations);
}
}