diff options
author | Jose <jose@zeroc.com> | 2012-09-20 01:12:42 +0200 |
---|---|---|
committer | Jose <jose@zeroc.com> | 2012-09-20 01:12:42 +0200 |
commit | 5042024423094c9b2a5685888c9be8cd53f6e61e (patch) | |
tree | d6f0ea21402d94c30bab546d0982f9875375cbea /java/src/IceGridGUI/LiveDeployment/GraphView.java | |
parent | Win32 fixes (diff) | |
download | ice-5042024423094c9b2a5685888c9be8cd53f6e61e.tar.bz2 ice-5042024423094c9b2a5685888c9be8cd53f6e61e.tar.xz ice-5042024423094c9b2a5685888c9be8cd53f6e61e.zip |
Graph & Metrics UI updates
Diffstat (limited to 'java/src/IceGridGUI/LiveDeployment/GraphView.java')
-rw-r--r-- | java/src/IceGridGUI/LiveDeployment/GraphView.java | 1861 |
1 files changed, 1001 insertions, 860 deletions
diff --git a/java/src/IceGridGUI/LiveDeployment/GraphView.java b/java/src/IceGridGUI/LiveDeployment/GraphView.java index 7de8214bb0c..6f11ba3628e 100644 --- a/java/src/IceGridGUI/LiveDeployment/GraphView.java +++ b/java/src/IceGridGUI/LiveDeployment/GraphView.java @@ -31,6 +31,9 @@ import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; +import java.awt.Frame; +import java.awt.Color; +import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -39,33 +42,52 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.AbstractAction; +import javax.swing.AbstractCellEditor; import javax.swing.Action; import javax.swing.BorderFactory; +import javax.swing.border.Border; +import javax.swing.DefaultCellEditor; +import javax.swing.DefaultListCellRenderer; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; +import javax.swing.event.TableModelEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.ListSelectionEvent; + import javax.swing.BoxLayout; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; +import javax.swing.JColorChooser; import javax.swing.JCheckBoxMenuItem; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; +import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTable; +import javax.swing.JToolBar; import javax.swing.JOptionPane; import javax.swing.KeyStroke; +import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; +import javax.swing.JSplitPane; +import javax.swing.ListSelectionModel; import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +import java.text.DecimalFormat; import javafx.application.Platform; @@ -89,39 +111,117 @@ import javafx.scene.layout.FlowPane; import javafx.scene.Scene; +import javafx.scene.input.MouseEvent; +import javafx.event.EventHandler; +import javafx.scene.input.MouseButton; +import javafx.scene.control.Tooltip; + import javafx.util.StringConverter; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.factories.Borders; import com.jgoodies.forms.builder.ButtonBarBuilder; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.looks.Options; +import com.jgoodies.looks.HeaderStyle; +import com.jgoodies.looks.BorderStyle; +import com.jgoodies.looks.plastic.PlasticLookAndFeel; import IceGrid.*; import IceGridGUI.*; +import IceGridGUI.LiveDeployment.MetricsViewEditor.MetricsViewInfo; +import IceGridGUI.LiveDeployment.MetricsViewEditor.MetricsCell; +import IceGridGUI.LiveDeployment.MetricsViewEditor.MetricsViewTransferableData; + +import java.util.prefs.Preferences; +import java.util.prefs.BackingStoreException; + public class GraphView extends JFrame { - - public enum GraphCategory + class WorkQueue extends Thread { - DispatchGraphCategory, - InvocationsGraphCategory, - ConnectionsGraphCategory, - ThreadsGraphCategory, - BandwidthGraphCategory - } + private class WorkItem + { + public WorkItem(Runnable runnable, boolean javafx) + { + this.runnable = runnable; + this.javafx = javafx; + } - public enum GraphType - { - TotalAverageGraphType, - CurrentCountGraphType, - BandwidhGraphType + Runnable runnable; + boolean javafx; + } + + public void run() + { + while(true) + { + WorkItem item = null; + synchronized(this) + { + while(_queue.isEmpty()) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + assert !_queue.isEmpty(); + item = _queue.remove(0); + } + + final java.util.concurrent.Semaphore sem = new java.util.concurrent.Semaphore(0); + final Runnable r = item.runnable; + if(item.javafx) + { + Platform.runLater(new Runnable() + { + @Override + public void run() + { + r.run(); + sem.release(); + } + }); + } + else + { + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + r.run(); + sem.release(); + } + }); + } + sem.acquireUninterruptibly(); + } + } + + synchronized public void enqueue(Runnable runnable, boolean javafx) + { + if(_queue.isEmpty()) + { + notify(); + } + _queue.add(new WorkItem(runnable, javafx)); + } + + private java.util.List<WorkItem> _queue = new java.util.LinkedList<WorkItem>(); } class TimeFormatter extends StringConverter<java.lang.Number> { - TimeFormatter() + TimeFormatter(String format) { + setDateFormat(format); _dateFormat.setTimeZone(TimeZone.getDefault()); } @@ -161,7 +261,7 @@ public class GraphView extends JFrame _dateFormat = new SimpleDateFormat(format); } - private DateFormat _dateFormat = new SimpleDateFormat(getDateFormat()); + private DateFormat _dateFormat; } class TransferHandler extends javax.swing.TransferHandler @@ -173,8 +273,7 @@ public class GraphView extends JFrame boolean supported = false; for(DataFlavor f : support.getDataFlavors()) { - if(f.getMimeType().equals(NodeMimeType) || f.getMimeType().equals(ServerMimeType) || - f.getMimeType().equals(MetricsViewMimeType)) + if(f.equals(MetricsViewTransferableData.dataFlavor())) { supported = true; break; @@ -198,22 +297,9 @@ public class GraphView extends JFrame { try { - if(flavor.getMimeType().equals(NodeMimeType)) + if(flavor.equals(MetricsViewTransferableData.dataFlavor())) { - Node node = (Node)t.getTransferData(flavor); - importNode(node); - break; - } - else if(flavor.getMimeType().equals(ServerMimeType)) - { - Server server = (Server)t.getTransferData(flavor); - importServer(server); - break; - } - else if(flavor.getMimeType().equals(MetricsViewMimeType)) - { - MetricsView metricsView = (MetricsView)t.getTransferData(flavor); - importMetricsView(metricsView); + addSeries((MetricsViewTransferableData)t.getTransferData(flavor)); break; } } @@ -229,205 +315,6 @@ public class GraphView extends JFrame } } - private void importNode(Node node) - { - List<Server> servers = node.getServers(); - for(Server s : servers) - { - importServer(s); - } - } - - private void importServer(Server server) - { - List<MetricsView> metrics = server.getMetrics(); - for(MetricsView m : metrics) - { - importMetricsView(m); - } - } - - private void importMetricsView(MetricsView metricsView) - { - Server server = (Server)metricsView.getParent(); - Node node = (Node)server.getParent(); - MetricsViewInfo s = new MetricsViewInfo(node.getId(), server.getId(), metricsView.getId()); - if(!_metrics.contains(s)) - { - if(_metrics.size() == 0) - { - startRefreshThread(); - } - _metrics.add(s); - addSeries(s, metricsView.data().get(_key)); - } - } - - class MetricsViewTargetInfo - { - MetricsViewTargetInfo(MetricsViewInfo parent, String id) - { - this.parent = parent; - this.id = id; - this.total = 0; - this.totalLifetime = 0; - } - - @Override - public boolean equals(Object other) - { - if(other == null) - { - return false; - } - else if(other == this) - { - return true; - } - else if(!(other instanceof MetricsViewTargetInfo)) - { - return false; - } - MetricsViewTargetInfo that = (MetricsViewTargetInfo)other; - return this.parent.equals(that.parent) && this.id.equals(that.id); - } - - @Override - public int hashCode() - { - return IceInternal.HashUtil.hashAdd(parent.hashCode(), id); - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append(parent.toString()); - sb.append(" - "); - sb.append(id); - return sb.toString(); - } - - public String id; - public MetricsViewInfo parent; - // - // Totals from last delta. - // - public long total; - public long totalLifetime; - }; - - class BandwidhMetricsInfo extends MetricsViewTargetInfo - { - BandwidhMetricsInfo(MetricsViewInfo parent, String id, boolean in) - { - super(parent, id); - this.in = in; - } - - @Override public boolean - equals(Object other) - { - if(other == null) - { - return false; - } - else if(other == this) - { - return true; - } - else if(!(other instanceof BandwidhMetricsInfo)) - { - return false; - } - BandwidhMetricsInfo that = (BandwidhMetricsInfo)other; - return super.equals(that) && this.in == that.in; - } - - @Override - public int hashCode() - { - return IceInternal.HashUtil.hashAdd(super.hashCode(), in); - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append(super.toString()); - sb.append(" - "); - sb.append((in ? "bytes received" : "bytes sent")); - return sb.toString(); - } - - public boolean in; - }; - - public class MetricsViewInfo - { - MetricsViewInfo(String node, String server, String view) - { - this.node = node; - this.server = server; - this.view = view; - } - - @Override - public boolean equals(Object other) - { - if(other == null) - { - return false; - } - else if(other == this) - { - return true; - } - else if(!(other instanceof MetricsViewInfo)) - { - return false; - } - MetricsViewInfo that = (MetricsViewInfo)other; - return this.node.equals(that.node) && this.server.equals(that.server) && this.view.equals(that.view); - } - - @Override - public int hashCode() - { - int h = IceInternal.HashUtil.hashAdd(5381, node); - h = IceInternal.HashUtil.hashAdd(h, server); - return IceInternal.HashUtil.hashAdd(h, view); - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append(node); - sb.append(" - "); - sb.append(server); - sb.append(" - "); - sb.append(view); - return sb.toString(); - } - - public String node; - public String server; - public String view; - } - - public class DeltaInfo - { - public DeltaInfo(long total, long totalLifetime) - { - this.total = total; - this.totalLifetime = totalLifetime; - } - - public long total; - public long totalLifetime; - } - private class RefreshThread extends Thread { RefreshThread(long period) @@ -446,11 +333,13 @@ public class GraphView extends JFrame { while(true) { - ArrayList<MetricsViewInfo> metrics = null; + + java.util.Set<MetricsViewInfo> metrics = null; synchronized(GraphView.this) { - metrics = new ArrayList<MetricsViewInfo>(_metrics); + metrics = new java.util.HashSet<MetricsViewInfo>(_series.keySet()); } + for(final MetricsViewInfo m : metrics) { Ice.Identity adminId = new Ice.Identity(m.server, _coordinator.getServerAdminCategory()); @@ -459,7 +348,7 @@ public class GraphView extends JFrame { public void response(final java.util.Map<java.lang.String, IceMX.Metrics[]> data) { - addDelta(m, data, System.currentTimeMillis()); + addData(m, data, System.currentTimeMillis()); } public void exception(final Ice.LocalException e) @@ -511,9 +400,10 @@ public class GraphView extends JFrame } } - private void handleError(final MetricsViewInfo metrics, final String error) + private void + handleError(final MetricsViewInfo metrics, final String error) { - SwingUtilities.invokeLater(new Runnable() + _queue.enqueue(new Runnable() { public void run() { @@ -521,33 +411,30 @@ public class GraphView extends JFrame "Error retrieving metrics view from `" + metrics.toString() + "'\n" + error, "Error", JOptionPane.ERROR_MESSAGE); - removeMetrics(metrics); + //removeMetrics(metrics); } - }); + }, false); } private long _period; private boolean _done = false; } - public GraphView(Coordinator coordinator, GraphType type, GraphCategory category, - final String title, String key) + public GraphView(Coordinator coordinator) { _coordinator = coordinator; - _type = type; - _category = category; - setTitle(title); - _key = key; + _queue = new WorkQueue(); + _queue.setDaemon(true); + _queue.start(); + + _preferences = Preferences.userNodeForPackage(getClass()); // // Don't destroy JavaFX when the frame is disposed. // Platform.setImplicitExit(false); - setIconImage(Utils.getIcon("/icons/16x16/grid.png").getImage()); - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) @@ -557,69 +444,6 @@ public class GraphView extends JFrame }); // - // Create actions - // - Action removeMetrics = new AbstractAction("Remove Metrics") - { - public void actionPerformed(ActionEvent event) - { - final DefaultTableModel model = new DefaultTableModel() - { - public Class getColumnClass(int column) - { - if(column == 3) - { - return Boolean.class; - } - else - { - return String.class; - } - } - - public boolean isCellEditable(int row, int column) - { - return column == 3; - } - }; - model.addColumn("Node"); - model.addColumn("Server"); - model.addColumn("View"); - model.addColumn("Show"); - - for(MetricsViewInfo m : _metrics) - { - model.addRow(new Object[]{m.node, m.server, m.view, true}); - } - - JTable table = new JTable(model); - table.setPreferredSize(new Dimension(450, 300)); - table.setPreferredScrollableViewportSize(table.getPreferredSize()); - table.setCellSelectionEnabled(false); - table.setOpaque(false); - JScrollPane scrollPane = new JScrollPane(table); - JOptionPane.showMessageDialog(GraphView.this, scrollPane, "Select Servers", - JOptionPane.PLAIN_MESSAGE); - - for(int i = 0; i < model.getRowCount(); ++i) - { - // - // Remove unselected metrics from the graph. - // - boolean selected = ((Boolean)model.getValueAt(i, 3)).booleanValue(); - if(!selected) - { - removeMetrics(new MetricsViewInfo((String)model.getValueAt(i, 0), - (String)model.getValueAt(i, 1), - (String)model.getValueAt(i, 2))); - - } - } - } - }; - removeMetrics.setEnabled(true); - - // // Graph preferences. // Action preferences = new AbstractAction("Preferences") @@ -665,33 +489,6 @@ public class GraphView extends JFrame xAxisPanel = builder.getPanel(); } - // - // JComboBox to select Y Axis units. - // - JComboBox<String> units = null; - JPanel yAxisPanel = null; - if(_type == GraphType.BandwidhGraphType || _type == GraphType.TotalAverageGraphType) - { - if(_type == GraphType.BandwidhGraphType) - { - units = new JComboBox<String>(_bandwidthUnits); - units.setSelectedIndex(getBandwidthUnitIndex()); - } - else - { - units = new JComboBox<String>(_timeUnits); - units.setSelectedIndex(getTimeUnitIndex()); - } - - - { - DefaultFormBuilder builder = - new DefaultFormBuilder(new FormLayout("pref,2dlu,pref:grow", "pref")); - builder.append("Units:", units); - yAxisPanel = builder.getPanel(); - } - } - FormLayout layout = new FormLayout("fill:pref:grow", "pref"); final DefaultFormBuilder builder = new DefaultFormBuilder(layout); builder.border(Borders.DIALOG); @@ -700,534 +497,502 @@ public class GraphView extends JFrame builder.nextLine(); builder.appendSeparator("Horizontal Axis"); builder.append(xAxisPanel); - if(yAxisPanel != null) + + if(JOptionPane.showConfirmDialog(GraphView.this, builder.getPanel(), "Graph Preferences", + JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) != JOptionPane.OK_OPTION) { - builder.appendSeparator("Vertical Axis"); - builder.append(yAxisPanel); + return; } - class PreferencesDialog extends JDialog - { - PreferencesDialog(JFrame owner) - { - super(owner, true); - setTitle("Graph Preferences"); - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - - JButton okButton = new JButton("OK"); - ActionListener okListener = new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - _option = JOptionPane.OK_OPTION; - setVisible(false); - dispose(); - } - }; + setRefreshPeriod(refreshPeriod.getNumber().longValue()); + setHorizontalSymbolsCount(horizontalAxisSymbolCount.getNumber().intValue()); + setDateFormat((String)dateFormats.getSelectedItem()); + } + }; - ActionListener cancelListener = new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - setVisible(false); - dispose(); - } - }; - - okButton.addActionListener(okListener); - getRootPane().setDefaultButton(okButton); - KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - getRootPane().registerKeyboardAction(cancelListener, stroke, - JComponent.WHEN_IN_FOCUSED_WINDOW); - - final JComponent buttonBar = new ButtonBarBuilder().addGlue() - .addButton(okButton) - .addGlue().build(); - buttonBar.setBorder(Borders.DIALOG); - - Container contentPane = getContentPane(); - contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); - contentPane.add(builder.getPanel()); - contentPane.add(buttonBar); - pack(); - setResizable(false); - setLocationRelativeTo(owner); - } + _legendTable = new JTable(_legendModel); - int showDialog() - { - setVisible(true); - return _option; - } + // + // Graph preferences. + // + final Action delete = new AbstractAction("Delete") + { + public void actionPerformed(ActionEvent event) + { + int[] selectedRows = _legendTable.getSelectedRows(); + for(int i = 0; i < selectedRows.length; ++i) + { + selectedRows[i] = _legendTable.convertRowIndexToModel(selectedRows[i]); + } - private int _option = JOptionPane.CLOSED_OPTION; - }; // - // Disabled the default dialog action, so we can use Enter for commit table cells edit. + // Remove selected rows from the legend model. // - PreferencesDialog dialog = new PreferencesDialog(GraphView.this); + final MetricsRow[] rows = _legendModel.removeRows(selectedRows); - if(dialog.showDialog() != JOptionPane.OK_OPTION) - { - return; - } - setRefreshPeriod(refreshPeriod.getNumber().longValue()); - setHorizontalSymbolsCount(horizontalAxisSymbolCount.getNumber().intValue()); - setDateFormat((String)dateFormats.getSelectedItem()); - if((_type == GraphType.BandwidhGraphType && getBandwidthUnitIndex() != units.getSelectedIndex()) || - _type == GraphType.TotalAverageGraphType && getTimeUnitIndex() != units.getSelectedIndex()) + // + // Remove rows from series hash maps + // + for(MetricsRow row : rows) { - final int index = units.getSelectedIndex(); - final float cf = conversionFactor(index); - final String label = yAxisLabel(index); - - if(_type == GraphType.BandwidhGraphType) - { - setBandwidthUnitIndex(index); - } - else + Map<String, Map<String, Map<String, MetricsRow>>> j = _series.get(row.info); + Map<String, Map<String, MetricsRow>> k = j.get(row.cell.getField().getMetricsName()); + Map<String, MetricsRow> l = k.get(row.cell.getId()); + l.remove(row.cell.getField().getFieldName()); + if(l.size() == 0) { - setTimeUnitIndex(index); - } - - // - // Update the graph in JavaFX thread. - // - Platform.runLater(new Runnable() + k.remove(row.cell.getId()); + if(k.size() == 0) { - @Override - public void run() + j.remove(row.cell.getField().getMetricsName()); + if(j.size() == 0) { - _yAxis.setLabel(label); - for(XYChart.Series<Number, Number> series : _chart.getData()) + _series.remove(row.info); + if(_series.size() == 0) { - for(XYChart.Data<Number, Number> item : series.getData()) - { - item.setYValue(item.getYValue().floatValue() * cf); - } + stopRefreshThread(); } } - }); + } + } } - } - }; - Action showLegend = new AbstractAction("Show Legend") - { - public void actionPerformed(ActionEvent event) - { - Platform.runLater(new Runnable() + // + // Remove series from the chart, in JavaFx thread. + // + _queue.enqueue(new Runnable() + { + @Override + public void run() { - @Override - public void run() + for(MetricsRow row : rows) { - _legendScroll.setVisible(!_legendScroll.isVisible()); + String seriesClass = getSeriesClass(row.series); + _styles.remove(seriesClass); + // + // Don't remove the XYChart.Series object here, to avoid the series style classes + // to be reasign by JavaFX. + // + // _chart.getData().remove(row.series); + row.series.getData().clear(); } - }); + } + }, true); } }; + delete.setEnabled(false); + + _legendTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() + { + public void valueChanged(ListSelectionEvent e) + { + if(!e.getValueIsAdjusting()) + { + delete.setEnabled(_legendTable.getSelectedRows().length > 0); + } + } + }); + + // + // Create the tool bar + // + class ToolBar extends JToolBar + { + public ToolBar() + { + putClientProperty(Options.HEADER_STYLE_KEY, HeaderStyle.BOTH); + putClientProperty(PlasticLookAndFeel.BORDER_STYLE_KEY, BorderStyle.SEPARATOR); + setFloatable(false); + putClientProperty("JToolBar.isRollover", Boolean.TRUE); + + JButton button = new JButton(delete); + button.setText(null); + button.setIcon(Utils.getIcon("/icons/24x24/delete.png")); + add(button); + } + } // // Create menus // JMenuBar menuBar = new JMenuBar(); - // Create a menu JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic(java.awt.event.KeyEvent.VK_F); - fileMenu.add(removeMetrics); fileMenu.add(preferences); menuBar.add(fileMenu); - JMenu viewMenu = new JMenu("View"); - viewMenu.setMnemonic(java.awt.event.KeyEvent.VK_V); - _showLegendMenuItem = new JCheckBoxMenuItem(showLegend); - _showLegendMenuItem.setState(false); - viewMenu.add(_showLegendMenuItem); - menuBar.add(viewMenu); + JMenu editMenu = new JMenu("Edit"); + editMenu.setMnemonic(java.awt.event.KeyEvent.VK_E); + editMenu.add(delete); + menuBar.add(editMenu); setJMenuBar(menuBar); - JLabel dropTarget = new JLabel("Drop runtime components here to add it to the graph."); - dropTarget.setBorder(new CompoundBorder(BorderFactory.createLineBorder(java.awt.Color.gray, 5, true), - new EmptyBorder(20, 20, 20, 20))); - dropTarget.setHorizontalAlignment(SwingConstants.CENTER); - Font f = dropTarget.getFont(); - dropTarget.setFont(f.deriveFont(f.getStyle(), f.getSize() + 8)); - dropTarget.setTransferHandler(new TransferHandler()); + // + // Set a combobox to edit the scale factors. + // + JComboBox<Double> scales = new JComboBox<Double>(_scales); + scales.setRenderer(new DecimalRenderer(scales.getRenderer())); + _legendTable.getColumnModel().getColumn(7).setCellEditor(new DefaultCellEditor(scales)); + + // + //Set default renderer and editor for Color.class column. + // + _legendTable.setDefaultRenderer(Color.class, new ColorRenderer(true)); + _legendTable.setDefaultEditor(Color.class, new ColorEditor()); + setTransferHandler(new TransferHandler()); + _legendTable.setAutoCreateRowSorter(true); final JFXPanel fxPanel = new JFXPanel(); + + // + // Build the split pane, with the chart graph and the legend table. + // + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + splitPane.setTopComponent(fxPanel); + splitPane.setBottomComponent(new JScrollPane(_legendTable)); + splitPane.setResizeWeight(0.9); + DefaultFormBuilder builder = new DefaultFormBuilder(new FormLayout("fill:pref:grow", "fill:pref:grow, pref")); - builder.append(fxPanel); - builder.nextLine(); - builder.append(dropTarget); + builder.append(splitPane); builder.nextLine(); JPanel panel = builder.getPanel(); panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - setContentPane(panel); + getContentPane().add(new ToolBar(), BorderLayout.PAGE_START); + getContentPane().add(panel, BorderLayout.CENTER); setPreferredSize(new Dimension(1024, 768)); - pack(); + // // initialize the scene in JavaFX thread. // - Platform.runLater(new Runnable() + _queue.enqueue(new Runnable() { @Override public void run() { _xAxis = new NumberAxis(); _yAxis = new NumberAxis(); - _legend = new VBox(5); - _legend.setPadding(new javafx.geometry.Insets(5, 5, 5, 5)); - _legend.setAlignment(javafx.geometry.Pos.CENTER); - _legendScroll = new ScrollPane(); - _legendScroll .managedProperty().bind(_legendScroll .visibleProperty()); - _legendScroll.setHbarPolicy(ScrollBarPolicy.NEVER); - _legendScroll.setVbarPolicy(ScrollBarPolicy.AS_NEEDED); - _legendScroll.setPrefWidth(295); - _legendScroll.setMaxWidth(295); - _legendScroll.setContent(_legend); - _legendScroll.setVisible(false); - - VBox legendBox = new VBox(5); - legendBox.setPadding(new javafx.geometry.Insets(10, 10, 10, 10)); - legendBox.setAlignment(javafx.geometry.Pos.CENTER); - VBox.setVgrow(_legendScroll, Priority.ALWAYS); - legendBox.getChildren().add(_legendScroll); _chart = new LineChart<Number, Number>(_xAxis, _yAxis); - _xAxis.setLabel("Time (HH:mm:ss)"); + _xAxis.setLabel("Time (" + getDateFormat() + ")"); _xAxis.setTickLabelFormatter(_timeFormater); _xAxis.setForceZeroInRange(false); - _yAxis.setLabel(yAxisLabel(getUnitsSelectedIndex())); - _chart.setTitle(title); _chart.setAnimated(true); _chart.setLegendVisible(false); - HBox hbox = new HBox(5); - hbox.setPadding(new javafx.geometry.Insets(0, 5, 0, 5)); - hbox.setAlignment(javafx.geometry.Pos.CENTER); - HBox.setHgrow(_chart, Priority.ALWAYS); - hbox.getChildren().addAll(_chart, legendBox); - fxPanel.setScene(new Scene(hbox)); + fxPanel.setScene(new Scene(_chart)); } - }); - setLocationRelativeTo(_coordinator.getMainFrame()); + }, true); + + pack(); + if(!loadPreferences()) + { + Rectangle otherRect = _coordinator.getMainFrame().getBounds(); + Rectangle thisRect = getBounds(); + if(otherRect.width < thisRect.width || otherRect.height < thisRect.height) + { + setLocationRelativeTo(null); + } + else + { + setLocationRelativeTo(_coordinator.getMainFrame()); + } + } setVisible(true); - } - public void close() - { - stopRefreshThread(); - setVisible(false); - _coordinator.removeGraphView(GraphView.this); - dispose(); - } + splitPane.setDividerLocation(0.8); - synchronized private void startRefreshThread() - { - if(_refreshThread == null) + // + // Show info dialog if required. + // + if(showInfo()) { - _refreshThread = new RefreshThread(getRefreshPeriod()); - _refreshThread.start(); + JCheckBox checkbox = new JCheckBox("Do not show this message again."); + String message = "Drop metrics cells on the table to add them to the graph."; + + JOptionPane.showConfirmDialog(this, new Object[]{message, checkbox}, "Information", + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE); + if(checkbox.isSelected()) + { + _preferences.node("GraphView").putBoolean("showInfo", false); + } } } - synchronized private void stopRefreshThread() + private boolean showInfo() { - if(_refreshThread != null) + try { - _refreshThread.done(); - _refreshThread = null; + if(!_preferences.nodeExists("GraphView")) + { + return true; + } } + catch(BackingStoreException ex) + { + return true; + } + return _preferences.node("GraphView").getBoolean("showInfo", true); } - synchronized private void setHorizontalSymbolsCount(int count) - { - _horizontaSymbolsCount = count; - } - - synchronized private int getHorizontalSymbolsCount() + public void storePreferences() { - return _horizontaSymbolsCount; + Preferences preferences = _preferences.node("GraphView"); + Rectangle rect = getBounds(); + preferences.putInt("x", rect.x); + preferences.putInt("y", rect.y); + preferences.putInt("width", rect.width); + preferences.putInt("height", rect.height); + preferences.putBoolean("maximized", getExtendedState() == Frame.MAXIMIZED_BOTH); } - void addDelta(MetricsViewInfo info, java.util.Map<java.lang.String, IceMX.Metrics[]> data, - final long timestamp) + public boolean loadPreferences() { - IceMX.Metrics[] metrics = data.get(_key); - Map<MetricsViewTargetInfo, XYChart.Series<Number, Number>> seriesSeq = _series.get(info); - if(seriesSeq == null) + try { - return; + if(!_preferences.nodeExists("GraphView")) + { + return false; + } + } + catch(BackingStoreException ex) + { + return false; } - if(metrics != null) + Preferences preferences = _preferences.node("GraphView"); + int x = preferences.getInt("x", 0); + int y = preferences.getInt("y", 0); + int width = preferences.getInt("width", 0); + int height = preferences.getInt("height", 0); + setBounds(new Rectangle(x, y, width, height)); + if(preferences.getBoolean("maximized", false)) { - if(_type == GraphType.TotalAverageGraphType) + setExtendedState(Frame.MAXIMIZED_BOTH); + } + return true; + } + + public void close() + { + storePreferences(); + stopRefreshThread(); + setVisible(false); + _coordinator.removeGraphView(GraphView.this); + dispose(); + } + + private void addSeries(final MetricsViewTransferableData data) + { + // + // Must run in JavaFX thread. + // + _queue.enqueue(new Runnable() { - for(IceMX.Metrics m : metrics) + @Override + public void run() { - MetricsViewTargetInfo targetInfo = new MetricsViewTargetInfo(info, m.id); - XYChart.Series<Number, Number> series = seriesSeq.get(targetInfo); - if(series == null) + Map<String, Map<String, Map<String, MetricsRow>>> metrics = _series.get(data.info); + if(metrics == null) { - continue; + metrics = new HashMap<String, Map<String, Map<String, MetricsRow>>>(); + _series.put(data.info, metrics); } - float value = 0; - DeltaInfo delta = _deltas.get(targetInfo); - if(delta == null) - { - delta = new DeltaInfo(m.total, m.totalLifetime); - _deltas.put(targetInfo, delta); - continue; // We need two meassurements to calculate delta AVG. - } - long deltaTotal = (m.total - delta.total); - value = deltaTotal == 0 ? 0.0f : - (m.totalLifetime - delta.totalLifetime) / deltaTotal; - delta.total = m.total; - delta.totalLifetime = m.totalLifetime; - addDelta(series, timestamp, value * _timeUnitsFactors[getTimeUnitIndex()]); - } - } - else if(_type == GraphType.CurrentCountGraphType) - { - for(IceMX.Metrics m : metrics) - { - MetricsViewTargetInfo targetInfo = new MetricsViewTargetInfo(info, m.id); - XYChart.Series<Number, Number> series = seriesSeq.get(targetInfo); - if(series == null) + Map<String, Map<String, MetricsRow>> rows = metrics.get(data.name); + if(rows == null) { - continue; + rows = new HashMap<String, Map<String, MetricsRow>>(); + metrics.put(data.name, rows); } - float value = 0; - value = m.current; - addDelta(series, timestamp, value); - } - } - else if(_type == GraphType.BandwidhGraphType) - { - for(IceMX.Metrics m : metrics) - { - IceMX.ConnectionMetrics cm = (IceMX.ConnectionMetrics)m; - for(boolean in : new boolean[]{true, false}) + + for(Map.Entry<String, List<MetricsCell>> i : data.rows.entrySet()) { - MetricsViewTargetInfo targetInfo = new BandwidhMetricsInfo(info, m.id, in); - XYChart.Series<Number, Number> series = seriesSeq.get(targetInfo); - if(series == null) + final String rowId = i.getKey(); + Map<String, MetricsRow> columns = rows.get(rowId); + if(columns == null) { - continue; + columns = new HashMap<String, MetricsRow>(); + rows.put(rowId, columns); } - float value = 0; - DeltaInfo delta = _deltas.get(targetInfo); - if(delta == null) + for(MetricsCell j : i.getValue()) { - if(in) + if(columns.get(j.getField().getFieldName()) == null) { - delta = new DeltaInfo(cm.receivedBytes, timestamp); - } - else - { - delta = new DeltaInfo(cm.sentBytes, timestamp); - } - _deltas.put(targetInfo, delta); - continue; // We need two meassurements to calculate delta AVG. - } + String color = DefaultColors[_chart.getData().size() % DefaultColors.length]; + final MetricsRow row = new MetricsRow(data.info, j, color, + new XYChart.Series<Number, Number>()); + _chart.getData().add(row.series); - if(in) - { - boolean zero = (cm.receivedBytes - delta.total) == 0 || - (timestamp - delta.totalLifetime) == 0; - value = zero ? 0.0f : (cm.receivedBytes - delta.total) / - (float)(timestamp - delta.totalLifetime); - delta.total = cm.receivedBytes; - delta.totalLifetime = timestamp; - } - else - { - boolean zero = (cm.sentBytes - delta.total) == 0 || - (timestamp - delta.totalLifetime) == 0; - value = zero ? 0.0f : (cm.sentBytes - delta.total) / - (float)(timestamp - delta.totalLifetime); - delta.total = cm.sentBytes; - delta.totalLifetime = timestamp; + String styleClass = getSeriesClass(row.series); + addStyle(row.series, styleClass, color); + setNodesStyle(styleClass); + + columns.put(j.getField().getFieldName(), row); + + // + // When a line is clicked we select the correspoding row in the legend table. + // + javafx.scene.Node n = _chart.lookup(".chart-series-line." + getSeriesClass(row.series)); + if(n != null) + { + n.setOnMousePressed(new EventHandler<MouseEvent>() + { + @Override public void + handle(MouseEvent e) + { + if(e.getEventType() == MouseEvent.MOUSE_PRESSED && + e.getButton() == MouseButton.PRIMARY) + { + // + // Must run in Swing thread. + // + _queue.enqueue(new Runnable() + { + public void run() + { + int i = _legendModel.getRowIndex(row); + if(i != -1) + { + i = _legendTable.convertRowIndexToView(i); + _legendTable.setRowSelectionInterval(i, i); + } + } + }, false); + } + } + }); + } + // + // Add the serie to the legend, must run in Swing thread. + // + _queue.enqueue(new Runnable() + { + public void run() + { + _legendModel.addRow(row); + } + }, false); + } } - addDelta(series, timestamp, value * _bandwidthUnitsFactors[getBandwidthUnitIndex()]); } - } - } - } - } - - private void addDelta(final XYChart.Series<Number, Number> series, final long timestamp, final float value) - { - // - // Update the graph series in JavaFX thread. - // - Platform.runLater(new Runnable() - { - @Override - public void run() - { - series.getData().add(new XYChart.Data<Number, Number>(timestamp, value)); - while(series.getData().size() > getHorizontalSymbolsCount()) + if(_chart.getData().size() > 0) { - series.getData().remove(0); + startRefreshThread(); } - updateStyle(series, getSeriesClass(series)); } - }); + }, true); } - public void addSeries(final MetricsViewInfo info, final IceMX.Metrics[] data) + private void addData(final MetricsViewInfo info, final Map<String, IceMX.Metrics[]> data, + final long timestamp) { // - // Need to run in JavaFX thread. + // Update the graph series in JavaFX thread. // - Platform.runLater(new Runnable() + _queue.enqueue(new Runnable() { @Override public void run() { - if(data != null) + Map<String, Map<String, Map<String, MetricsRow>>> metricsCollection = _series.get(info); + if(metricsCollection != null) { - Map<MetricsViewTargetInfo, XYChart.Series<Number, Number>> seriesSeq = _series.get(info); - - if(seriesSeq == null) - { - seriesSeq = new HashMap<MetricsViewTargetInfo, XYChart.Series<Number, Number>>(); - _series.put(info, seriesSeq); - } - - if(_category != GraphCategory.BandwidthGraphCategory) + for(Map.Entry<String, IceMX.Metrics[]> i : data.entrySet()) { - for(IceMX.Metrics m : data) + Map<String, Map<String, MetricsRow>> rows = metricsCollection.get(i.getKey()); + if(rows == null) { - String color = DefaultColors[_chart.getData().size() % DefaultColors.length]; - MetricsViewTargetInfo targetInfo = new MetricsViewTargetInfo(info, m.id); - XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>(); - series.setName(targetInfo.toString()); - seriesSeq.put(targetInfo, series); - _chart.getData().add(series); - String styleClass = getSeriesClass(series); - addStyle(series, styleClass, color, false); - updateStyle(series, styleClass); - seriesAdded(targetInfo.toString(), styleClass); + continue; } - } - else - { - for(IceMX.Metrics m : data) + + for(IceMX.Metrics metrics : i.getValue()) { - String color = DefaultColors[(_chart.getData().size() / 2) % DefaultColors.length]; - for(boolean in : new boolean[]{true, false}) + Map<String, MetricsRow> columns = rows.get(metrics.id); + if(columns == null) { - MetricsViewTargetInfo targetInfo = new BandwidhMetricsInfo(info, m.id, in); - XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>(); - series.setName(targetInfo.toString()); - seriesSeq.put(targetInfo, series); - _chart.getData().add(series); - String styleClass = getSeriesClass(series); - addStyle(series, styleClass, color, in); - updateStyle(series, styleClass); - seriesAdded(targetInfo.toString(), styleClass); + continue; + } + + for(Map.Entry<String, MetricsRow> j : columns.entrySet()) + { + try + { + MetricsRow row = j.getValue(); + Number value = row.cell.getValue(metrics); + if(value == null) + { + continue; + } + row.series.getData().add(new XYChart.Data<Number, Number>( + timestamp, + value)); + + final int n = getHorizontalSymbolsCount(); + while(row.series.getData().size() > n) + { + row.series.getData().remove(0); + } + + // + // Set the style so that new created nodes has the right style. + // + final String cssClass = getSeriesClass(row.series); + setNodesStyle(cssClass); + + // + // If the series isn't visible ensure that new created nodes are hidden. + // + if(!row.visible) + { + setNodesVisible(cssClass, false); + } + } + catch(java.lang.RuntimeException ex) + { + ex.printStackTrace(); + } } } } + // + // Fire an event on the legend model to update all cells. + // + _queue.enqueue(new Runnable() + { + public void run() + { + _legendModel.fireTableChanged( + new TableModelEvent(_legendModel, 0, _legendModel.getRowCount() - 1, + TableModelEvent.ALL_COLUMNS, TableModelEvent.UPDATE)); + } + }, false); } } - }); + }, true); } - // - // Must be called in JavaFX thread. - // - // Return the class used to style a serie. - // - public String getSeriesClass(XYChart.Series<Number, Number> series) - { - String value = null; - for(String styleClass : series.getNode().getStyleClass()) - { - if(styleClass.startsWith("series")) - { - value = styleClass; - break; - } - } - return value; - } - - private void addStyle(XYChart.Series<Number, Number> series, String seriesClass, String color, boolean dashed) + synchronized private void startRefreshThread() { - // - // Customize the style. - // - StringBuilder sb = new StringBuilder(); - sb.append("-fx-stroke: "); - sb.append(color); - sb.append("; "); - sb.append("-fx-background-color: "); - sb.append(color); - sb.append(", white;"); - if(dashed) + if(_refreshThread == null) { - sb.append("-fx-stroke-dash-array: 10 10;"); + _refreshThread = new RefreshThread(getRefreshPeriod()); + _refreshThread.start(); } - sb.append("-fx-stroke-width: 5;"); - _styles.put(seriesClass, sb.toString()); } - private void - removeMetrics(MetricsViewInfo info) + synchronized private void stopRefreshThread() { - _metrics.remove(info); - if(_metrics.size() == 0) + if(_refreshThread != null) { - stopRefreshThread(); + _refreshThread.done(); + _refreshThread = null; } - removeSeries(info); - } - - // - // Remove all series associated to a Metrics View - // - private void removeSeries(final MetricsViewInfo info) - { - // - // Need to run in JavaFX thread. - // - Platform.runLater(new Runnable() - { - @Override - public void run() - { - Map<MetricsViewTargetInfo, XYChart.Series<Number, Number>> seriesSeq = _series.get(info); - if(seriesSeq != null) - { - for(Map.Entry<MetricsViewTargetInfo, XYChart.Series<Number, Number>> entry : - seriesSeq.entrySet()) - { - String seriesClass = getSeriesClass(entry.getValue()); - seriesRemoved(seriesClass); - _styles.remove(seriesClass); - // - // Don't remove the XYChart.Series object here, to avoid the series style classes - // to be reasign by JavaFX. - // - // _chart.getData().remove(entry.getValue()); - entry.getValue().getData().clear(); - } - _deltas.remove(info); - _series.remove(info); - } - } - }); } synchronized long getRefreshPeriod() @@ -1253,106 +1018,385 @@ public class GraphView extends JFrame { _dateFormat = dateFormat; _timeFormater.setDateFormat(dateFormat); + // + // Update the horizontal axis label, in JavaFx thread. + // + _queue.enqueue(new Runnable() + { + @Override + public void run() + { + _xAxis.setLabel("Time (" + getDateFormat() + ")"); + } + }, true); } - synchronized String getBandwithUnit() + synchronized private void setHorizontalSymbolsCount(final int count) { - return _bandwidthUnits[_bandwidthUnitIndex]; + if(count < _horizontaSymbolsCount) + { + // + // When the number of symbolls decreases, we remove the oldest symbols + // of all series. + // + _queue.enqueue(new Runnable() + { + @Override + public void run() + { + for(XYChart.Series<Number, Number> series : _chart.getData()) + { + while(series.getData().size() > count) + { + series.getData().remove(0); + } + } + } + }, true); + } + _horizontaSymbolsCount = count; + } - synchronized int getBandwidthUnitIndex() + synchronized private int getHorizontalSymbolsCount() { - return _bandwidthUnitIndex; + return _horizontaSymbolsCount; } - synchronized void setBandwidthUnitIndex(int index) + class MetricsRow { - _bandwidthUnitIndex = index; - } + public MetricsRow(MetricsViewInfo info, MetricsCell cell, String color, XYChart.Series<Number, Number> series) + { + this.visible = true; + this.info = info; + this.cell = cell; + this.color = color; + this.series = series; + } - synchronized String getTimeUnit() - { - return _timeUnits[_timeUnitIndex]; + boolean visible; + MetricsViewInfo info; + MetricsCell cell; + String color; + XYChart.Series<Number, Number> series; } - synchronized int getTimeUnitIndex() + class LegendTableModel extends javax.swing.table.AbstractTableModel { - return _timeUnitIndex; - } + public String getColumnName(int col) + { + return _columnNames[col]; + } - synchronized void setTimeUnitIndex(int index) - { - _timeUnitIndex = index; - } + public int getRowCount() + { + return _rows.size(); + } - synchronized float conversionFactor(int index) - { - return _type == GraphType.BandwidhGraphType ? - _bandwidthUnitsFactors[index] / _bandwidthUnitsFactors[_bandwidthUnitIndex] : - _timeUnitsFactors[index] / _timeUnitsFactors[_timeUnitIndex]; - } + public int getColumnCount() + { + return _columnNames.length; + } - synchronized String yAxisLabel(int index) - { - if(_type == GraphType.BandwidhGraphType) + public Class getColumnClass(int columnIndex) + { + switch(columnIndex) + { + case 0: // Node Name + { + return Boolean.class; + } + case 1: // Node Name + { + return String.class; + } + case 2: // Server Name + { + return String.class; + } + case 3: // View Name + { + return String.class; + } + case 4: // Metrics Name + { + return String.class; + } + case 5: // Metrics Id + { + return String.class; + } + case 6: // Column Name + { + return String.class; + } + case 7: // Scale factor + { + return Double.class; + } + case 8: // Last value + { + return Double.class; + } + case 9: // Average value + { + return Double.class; + } + case 10: // Min value + { + return Double.class; + } + case 11: // Max value + { + return Double.class; + } + case 12: // Color + { + return Color.class; + } + default: + { + return null; + } + } + } + + public Object getValueAt(int rowIndex, int columnIndex) { - return _bandwidthUnits[index]; + if(rowIndex > _rows.size() || columnIndex > _columnNames.length) + { + return null; + } + MetricsRow row = _rows.get(rowIndex); + switch(columnIndex) + { + case 0: + { + return row.visible; + } + case 1: + { + return row.info.node; + } + case 2: + { + return row.info.server; + } + case 3: + { + return row.info.view; + } + case 4: + { + return row.cell.getField().getMetricsName(); + } + case 5: + { + return row.cell.getId(); + } + case 6: + { + return row.cell.getField().getColumnName(); + } + case 7: + { + return row.cell.getScaleFactor(); + } + case 8: + { + return row.cell.getLast(); + } + case 9: + { + return row.cell.getAverage(); + } + case 10: + { + return row.cell.getMin(); + } + case 11: + { + return row.cell.getMax(); + } + case 12: + { + return new Color(Integer.parseInt(row.color.substring(1), 16)); + } + default: + { + return null; + } + } } - else if(_type == GraphType.TotalAverageGraphType) + + public boolean isCellEditable(int row, int col) { - StringBuilder sb = new StringBuilder(); - sb.append("Average "); - if(_category == GraphCategory.DispatchGraphCategory) + if(col < _columnNames.length && (_columnNames[col].equals("Show") || + _columnNames[col].equals("Scale") || + _columnNames[col].equals("Color"))) { - sb.append("dispatch "); + return true; } else { - sb.append("invocation "); + return false; } - sb.append("time ("); - sb.append(_timeUnits[index]); - sb.append(")"); - return sb.toString(); } - else if(_category == GraphCategory.ThreadsGraphCategory) + + public void setValueAt(final Object value, int rowIndex, int columnIndex) { - return "Threads"; + if(isCellEditable(rowIndex, columnIndex)) + { + final MetricsRow row = _rows.get(rowIndex); + if(_columnNames[columnIndex].equals("Show")) + { + row.visible = ((Boolean)value).booleanValue(); + _queue.enqueue(new Runnable() + { + @Override + public void run() + { + setNodesVisible(getSeriesClass(row.series), row.visible); + } + }, true); + } + else if(_columnNames[columnIndex].equals("Scale")) + { + double s1 = ((Double)getValueAt(rowIndex, columnIndex)).doubleValue(); + double s2 = ((Double)value).doubleValue(); + updateScaleFactor(row.series, s1, s2); + row.cell.setScaleFactor(((Double)value).doubleValue()); + } + else if(_columnNames[columnIndex].equals("Color")) + { + Color color = (Color)value; + // + // Convert color to the CSS representation used by JavaFX style. + // example: #ff00aa + // + row.color = "#" + String.format("%02X", color.getRed()) + + String.format("%02X", color.getGreen()) + + String.format("%02X", color.getBlue()); + updateSeriesColor(row.series, row.color); + } + fireTableCellUpdated(rowIndex, columnIndex); + } } - else if(_category == GraphCategory.ConnectionsGraphCategory) + + public void addRow(MetricsRow row) { - return "Connections"; + int i = _rows.size(); + _rows.put(i, row); + fireTableRowsInserted(i, i); } - else + + public MetricsRow[] removeRows(int[] rowIndexes) { - return null; + MetricsRow[] deletedRows = new MetricsRow[rowIndexes.length]; + for(int i = 0; i < rowIndexes.length; i++) + { + deletedRows[i] = _rows.remove(rowIndexes[i]); + } + Map<Integer, MetricsRow> rows = new HashMap<Integer, MetricsRow>(); + for(Map.Entry<Integer, MetricsRow> e : _rows.entrySet()) + { + rows.put(rows.size(), e.getValue()); + } + _rows = rows; + fireTableDataChanged(); + return deletedRows; } - } - synchronized int getUnitsSelectedIndex() - { - if(_type == GraphType.BandwidhGraphType) + public MetricsRow[] getRows(int[] rowIndexes) { - return _bandwidthUnitIndex; + MetricsRow[] rows = new MetricsRow[rowIndexes.length]; + for(int i = 0; i < rowIndexes.length; i++) + { + rows[i] = _rows.get(rowIndexes[i]); + } + return rows; } - else if(_type == GraphType.TotalAverageGraphType) + + public int getRowIndex(MetricsRow row) { - return _timeUnitIndex; + int index = -1; + for(Map.Entry<Integer, MetricsRow> entry : _rows.entrySet()) + { + if(row == entry.getValue()) + { + index = entry.getKey(); + break; + } + } + return index; } - else + + private Map<Integer, MetricsRow> _rows = new HashMap<Integer, MetricsRow>(); + } + + void + updateScaleFactor(final XYChart.Series<Number, Number> series, final double s1, final double s2) + { + // + // Must run in JavaFX thread. + // + _queue.enqueue(new Runnable() + { + @Override + public void run() + { + for(XYChart.Data<Number, Number> i : series.getData()) + { + i.setYValue(i.getYValue().doubleValue() * s2 / s1); + } + } + }, true); + } + + void + updateSeriesColor(final XYChart.Series<Number, Number> series, final String color) + { + // + // Must run in JavaFX thread. + // + _queue.enqueue(new Runnable() + { + @Override + public void run() + { + String cssClass = getSeriesClass(series); + addStyle(series, cssClass, color); + setNodesStyle(cssClass); + } + }, true); + } + + // + // Must be called in JavaFX thread. + // + // Return the class used to style a serie. + // + public String getSeriesClass(XYChart.Series<Number, Number> series) + { + String value = null; + for(String styleClass : series.getNode().getStyleClass()) { - return 0; + if(styleClass.startsWith("series")) + { + value = styleClass; + break; + } } + return value; } // // Must be called on JavaFX thread // - private void updateStyle(final XYChart.Series<Number, Number> series, String seriesClass) + private void setNodesStyle(String seriesClass) { String style = _styles.get(seriesClass); - java.util.Set<javafx.scene.Node> nodes = _chart.lookupAll("." + seriesClass); - for(javafx.scene.Node n : nodes) + for(javafx.scene.Node n : _chart.lookupAll("." + seriesClass)) { n.setStyle(style); } @@ -1361,106 +1405,175 @@ public class GraphView extends JFrame // // Must be called on JavaFX thread // - private void - seriesAdded(String name, String styleClass) + private void setNodesVisible(String seriesClass, boolean visible) { - VBox vbox = new VBox(5); - vbox.setPadding(new javafx.geometry.Insets(5, 5, 5, 5)); + for(javafx.scene.Node n : _chart.lookupAll("." + seriesClass)) + { + n.setVisible(visible); + } + } - Line line = new Line(0, 10, 250, 10); - line.setStyle(_styles.get(styleClass)); - - - Label label = new Label(name); - label.setPrefWidth(250); - label.setMaxWidth(250); - label.setWrapText(true); + // + // Must be called on JavaFX thread + // + private void addStyle(XYChart.Series<Number, Number> series, String seriesClass, String color) + { + // + // Customize the style. + // + StringBuilder sb = new StringBuilder(); + sb.append("-fx-stroke: "); + sb.append(color); + sb.append("; "); + sb.append("-fx-background-color: "); + sb.append(color); + sb.append(", white;"); + sb.append("-fx-stroke-width: 3;"); + _styles.put(seriesClass, sb.toString()); + } - if(!_legendScroll.isVisible() && _legend.getChildren().size() == 0) + static class DecimalRenderer extends DefaultListCellRenderer + { + public DecimalRenderer(ListCellRenderer renderer) { - _legendScroll.setVisible(true); - SwingUtilities.invokeLater(new Runnable() - { - public void run() - { - _showLegendMenuItem.setState(true); - } - }); + this._renderer = renderer; + } + + @Override + public Component + getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) + { + @SuppressWarnings("unchecked") + Component c = _renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if(c instanceof JLabel) + { + ((JLabel) c).setText(_format.format(value)); + } + else + { + c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + setText(_format.format(value)); + } + return c; } - vbox.getChildren().addAll(line, label); - vbox.setStyle("-fx-border-color: #c0c0c0;; -fx-border-radius: 5; -fx-background-color: #f5f5f5;"); - _legend.getChildren().add(vbox); - _legendItems.put(styleClass, vbox); + private ListCellRenderer _renderer; + private static final DecimalFormat _format = new DecimalFormat("#,###,###,##0.00#######"); } - private void - seriesRemoved(String styleClass) + public class ColorEditor extends AbstractCellEditor implements TableCellEditor, ActionListener { - VBox vbox = _legendItems.get(styleClass); - if(vbox != null) + public ColorEditor() + { + _button = new JButton(); + _button.setActionCommand(EDIT); + _button.addActionListener(this); + _button.setBorderPainted(false); + + _colorChooser = new JColorChooser(); + _dialog = JColorChooser.createDialog(_button, "Select the series color", true, _colorChooser, this, null); + + } + + public void actionPerformed(ActionEvent e) { - _legendItems.remove(styleClass); - _legend.getChildren().remove(vbox); - if(_legendScroll.isVisible() && _legend.getChildren().size() == 0) + if(EDIT.equals(e.getActionCommand())) + { + _button.setBackground(_currentColor); + _colorChooser.setColor(_currentColor); + _dialog.setVisible(true); + //Make the renderer reappear. + fireEditingStopped(); + } + else { - _legendScroll.setVisible(false); - SwingUtilities.invokeLater(new Runnable() + _currentColor = _colorChooser.getColor(); + } + } + + public Object getCellEditorValue() + { + return _currentColor; + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, + int row, int column) + { + _currentColor = (Color)value; + return _button; + } + + Color _currentColor; + JButton _button; + JColorChooser _colorChooser; + JDialog _dialog; + protected static final String EDIT = "edit"; + } + + static class ColorRenderer extends JLabel implements TableCellRenderer + { + Border unselectedBorder = null; + Border selectedBorder = null; + boolean isBordered = true; + + public ColorRenderer(boolean isBordered) + { + this.isBordered = isBordered; + setOpaque(true); //MUST do this for background to show up. + } + + public Component getTableCellRendererComponent(JTable table, Object color, boolean isSelected, boolean hasFocus, + int row, int column) + { + Color newColor = (Color)color; + setBackground(newColor); + if(isBordered) + { + if(isSelected) + { + if(selectedBorder == null) { - public void run() - { - _showLegendMenuItem.setState(false); - } - }); + selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getSelectionBackground()); + } + setBorder(selectedBorder); + } + else + { + if(unselectedBorder == null) + { + unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getBackground()); + } + setBorder(unselectedBorder); + } } + return this; } } private final Coordinator _coordinator; private RefreshThread _refreshThread; - private final List<MetricsViewInfo> _metrics = new ArrayList<MetricsViewInfo>(); - private Map<MetricsViewInfo, Map<MetricsViewTargetInfo, XYChart.Series<Number, Number>>> _series = - new HashMap<MetricsViewInfo, Map<MetricsViewTargetInfo, XYChart.Series<Number, Number>>>(); - - private Map<MetricsViewTargetInfo, DeltaInfo> _deltas = new HashMap<MetricsViewTargetInfo, DeltaInfo>(); - private Map<String, String> _styles = new HashMap<String, String>(); - private Map<String, VBox> _legendItems = new HashMap<String, VBox>(); - - private LineChart<Number, Number> _chart; - private ScrollPane _legendScroll; - private VBox _legend; - private final GraphCategory _category; - private final GraphType _type; - private final String _key; - private int _horizontaSymbolsCount = 10; + private int _horizontaSymbolsCount = 10; private long _refreshPeriod = 5000; private String[] _dateFormats = new String[]{"HH:mm:ss", "mm:ss"}; private String _dateFormat = _dateFormats[0]; + private final TimeFormatter _timeFormater = new TimeFormatter(_dateFormat); + private final Object _monitor = new Object(); - private String[] _bandwidthUnits = new String[]{"Bytes/s", "KBytes/s", "Bytes/min", "KBytes/min"}; - private float[] _bandwidthUnitsFactors = new float[]{1000.0f, 1.0f, 60000.0f, 60.0f}; - private int _bandwidthUnitIndex = 0; - - private String[] _timeUnits = new String[]{"\u00B5s", "ms", "s"}; - private float [] _timeUnitsFactors = new float[]{1.0f, 0.001f, 0.000001f}; - private int _timeUnitIndex = 1; - - final TimeFormatter _timeFormater = new TimeFormatter(); - final Object _monitor = new Object(); + private LineChart<Number, Number> _chart; + private NumberAxis _xAxis; + private NumberAxis _yAxis; - private JCheckBoxMenuItem _showLegendMenuItem; + private final static String[] _columnNames = new String[]{"Show", "Node", "Server", "Metrics View Name", + "Metrics Name", "Metrics Id", "Metrics Field", "Scale", + "Last", "Average", "Minimum", "Maximum", "Color"}; - NumberAxis _xAxis; - NumberAxis _yAxis; + private final Map<MetricsViewInfo, Map<String, Map<String, Map<String, MetricsRow>>>> _series = + new HashMap<MetricsViewInfo, Map<String, Map<String, Map<String, MetricsRow>>>>(); - private final static String NodeMimeType = - "application/x-java-jvm-local-objectref; class=IceGridGUI.LiveDeployment.Node"; - private final static String ServerMimeType = - "application/x-java-jvm-local-objectref; class=IceGridGUI.LiveDeployment.Server"; - private final static String MetricsViewMimeType = - "application/x-java-jvm-local-objectref; class=IceGridGUI.LiveDeployment.MetricsView"; + private final static String MetricsCellFlavor = + "application/x-icegridadmin-metrics-cells; class=java.io.InputStream"; private final static String[] DefaultColors = new String[] { @@ -1481,4 +1594,32 @@ public class GraphView extends JFrame "#00BFFF", // DeepSkyBlue "#4B0082", // Indigo }; + + private final JTable _legendTable; + private final LegendTableModel _legendModel = new LegendTableModel(); + + private final Map<String, String> _styles = new HashMap<String, String>(); + + private final Double[] _scales = new Double[]{0.000000001d, + 0.00000001d, + 0.0000001d, + 0.000001d, + 0.00001d, + 0.0001d, + 0.001d, + 0.01d, + 0.1d, + 1.0d, + 10.0d, + 100.0d, + 1000.0d, + 10000.0d, + 100000.0d, + 1000000.0d, + 10000000.0d, + 100000000.0d, + 1000000000.0d}; + private final WorkQueue _queue; + private final Preferences _preferences; } + |