blob: 60dd165d1b01f528ad6dcd462150e9a600a6f0ae [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation
*
* 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 org.apache.felix.mosgi.managedelements.memoryprobe.tab;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.DefaultXYItemRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
//import org.openide.windows.TopComponent;
//import org.mc4j.console.dashboard.components.RefreshControlComponent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public abstract class AbstractGraphPanel extends JComponent {
protected TimeSeriesCollection dataset = new TimeSeriesCollection();
protected JFreeChart chart;
protected Timer dataGeneratorTimer;
protected TimerTask dataGeneratorTimerTask;
protected Map timeSeriesMap = new HashMap();
protected XYPlot xyplot;
private JCheckBox controlsButton;
private JPanel controlsPanel;
private ButtonGroup buttonGroupScale;
private ButtonGroup buttonGroupTimeRange;
private JRadioButton jRadioButtonScaleLinear;
private JRadioButton jRadioButtonScaleLogarithmic;
private JRadioButton jRadioTimeHours;
private JRadioButton jRadioTimeMinutes;
private JRadioButton jRadioTimeSeconds;
protected LogarithmicTimeJSlider sleepSlider;
protected JLabel sleepDelay;
private boolean removed = false;
protected int failures = 0;
protected static final int MAX_FAILURES = 10;
public AbstractGraphPanel() {
setDoubleBuffered(false);
initGraphPanel();
}
public void setChartTitle(final String name) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setName(name);
}
});
chart.setTitle(
new TextTitle(name,
new Font("SansSerif",Font.BOLD, 12)));
}
protected void initGraphPanel() {
setPreferredSize(new Dimension(500,450));
DateAxis domain = new DateAxis("Time");
NumberAxis range = new NumberAxis("");
this.xyplot = new XYPlot(dataset, domain, range, new DefaultXYItemRenderer());
domain.setAutoRange(true);
domain.setLowerMargin(0.0);
domain.setUpperMargin(0.0);
domain.setTickLabelsVisible(true);
range.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
this.chart =
new JFreeChart(
"",
JFreeChart.DEFAULT_TITLE_FONT,
xyplot,
true);
chart.setTitle(
new TextTitle("Graph ???",
new Font("SansSerif",Font.BOLD, 12)));
ChartPanel chartPanel = new ChartPanel(chart,false,true,true,false,false);
chartPanel.setOpaque(true);
chartPanel.setDoubleBuffered(false);
chart.setBackgroundPaint(this.getBackground());
chartPanel.setBackground(this.getBackground());
chartPanel.setBorder(new LineBorder(Color.BLACK,1));
setLayout(new BorderLayout());
setOpaque(false);
add(chartPanel,BorderLayout.CENTER);
buildGraphControls();
this.controlsButton = new ControlsPopupButton();
JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
northPanel.add(this.controlsButton);
northPanel.setOpaque(false);
add(northPanel,BorderLayout.NORTH);
this.controlsPanel.setLocation(5,5);
doLayout();
repaint();
}
public class ControlsPopupButton extends JCheckBox {
private JPopupMenu popup;
private Icon descendingIcon = new ImageIcon(java.awt.Toolkit.getDefaultToolkit().getImage(MemoryProbeTabUI.bc.getBundle().getResource("images/GraphSettings.gif")));
public ControlsPopupButton() {
setIcon(descendingIcon);
setOpaque(false);
init();
}
public void showPopup() {
if (popup == null) {
popup = new JPopupMenu();
popup.add(controlsPanel);
}
popup.show(controlsButton, 0, controlsButton.getHeight());
}
private void init() {
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showPopup();
}
});
}
}
public long getUpdateDelay() {
if (sleepSlider != null)
return sleepSlider.getValue();
else
return 1000L;
}
/**
* Cancels any existing scheduled task and reschedules for the
* current status of the delay control.
*/
public void reschedule() {
if (this.dataGeneratorTimer == null) {
this.dataGeneratorTimer = new Timer();
}
if (this.dataGeneratorTimerTask != null) {
try {
this.dataGeneratorTimerTask.cancel();
} catch(Exception e) { e.printStackTrace();}
}
this.dataGeneratorTimer.schedule(
this.dataGeneratorTimerTask = getDataGeneratorTask(),
getUpdateDelay(),
getUpdateDelay());
}
/**
* Stops taking measurements for the graph.
*/
public void pauseSchedule() {
if (this.dataGeneratorTimer !=null){
this.dataGeneratorTimerTask.cancel();
}
this.dataGeneratorTimerTask = null;
}
protected TimerTask getDataGeneratorTask() {
return new DataGenerator();
}
public void addNotify() {
super.addNotify();
if (removed) {
reschedule();
this.removed = false;
}
}
public void removeNotify() {
super.removeNotify();
if (this.dataGeneratorTimer != null)
this.dataGeneratorTimer.cancel();
this.dataGeneratorTimer = null;
this.removed = true;
}
/**
* Define this graph as logarithmic or not
* @param logarithmic if true, graph will be logarithmic,
* otherwise it will be linear
*/
public void setLogarithmic(boolean logarithmic) {
if (logarithmic) {
try {
this.xyplot.setRangeAxis(new LogarithmicAxis(""));
} catch(Exception e) {
// In case we try to use a logarithmic axis when the values go below 1
this.xyplot.setRangeAxis(new NumberAxis(""));
}
} else {
this.xyplot.setRangeAxis(new NumberAxis(""));
}
}
public void setBackground(Color bg) {
super.setBackground(bg);
//this.sleepSlider.setBackground(bg);
}
public void updateRange() {
DateAxis dateAxis = (DateAxis) this.xyplot.getDomainAxis();
Calendar start = new GregorianCalendar();
start.setTime(new Date());
if (this.jRadioTimeSeconds.isSelected()) {
start.add(Calendar.SECOND, -60);
} else if (this.jRadioTimeMinutes.isSelected()) {
//this.sleepSlider.setEnabled(false);
start.add(Calendar.MINUTE, -60);
} else if (this.jRadioTimeHours.isSelected()) {
start.add(Calendar.HOUR, -48);
}
// this is a hack to slow down the data gathering for the longer periods...
// TODO GH: Build a TimeSeries that modulates the time periods automatically
// i.e.
// | Seconds | Minutes | Hours |
// ***************** * * * * * * * * * * * * *
if (this.jRadioTimeMinutes.isSelected()) {
sleepSlider.setValue(10000);
} else if (this.jRadioTimeHours.isSelected()) {
sleepSlider.setValue(100000);
}
dateAxis.setRange(start.getTime(),new Date());
}
protected JPanel buildGraphControls() {
this.controlsPanel = new JPanel();
//this.controlsPanel.setBorder(BorderFactory.createLineBorder(Color.black));
this.controlsPanel.setLayout(new BoxLayout(this.controlsPanel,BoxLayout.X_AXIS));
// Seconds, Minutes, Hours
JPanel timeDurationPanel = new JPanel();
timeDurationPanel.setLayout(new BoxLayout(timeDurationPanel, BoxLayout.Y_AXIS));
timeDurationPanel.setBorder(BorderFactory.createTitledBorder("Time Range"));
buttonGroupTimeRange = new javax.swing.ButtonGroup();
jRadioTimeSeconds = new javax.swing.JRadioButton("seconds", true);
jRadioTimeSeconds.setToolTipText("Track the graph for 60 seconds");
jRadioTimeSeconds.setOpaque(false);
jRadioTimeMinutes = new javax.swing.JRadioButton("minutes", false);
jRadioTimeMinutes.setToolTipText("Track this graph for 60 minutes.");
jRadioTimeMinutes.setOpaque(false);
jRadioTimeHours = new javax.swing.JRadioButton("hours", false);
jRadioTimeHours.setToolTipText("Track this graph for 48 hours.");
jRadioTimeHours.setOpaque(false);
buttonGroupTimeRange.add(jRadioTimeSeconds);
buttonGroupTimeRange.add(jRadioTimeMinutes);
buttonGroupTimeRange.add(jRadioTimeHours);
timeDurationPanel.add(jRadioTimeSeconds);
timeDurationPanel.add(jRadioTimeMinutes);
timeDurationPanel.add(jRadioTimeHours);
// Scale (linear vs. logarithmic)
JPanel timeScalePanel = new JPanel();
timeScalePanel.setLayout(new BoxLayout(timeScalePanel, BoxLayout.Y_AXIS));
timeScalePanel.setBorder(BorderFactory.createTitledBorder("Time Scale"));
buttonGroupScale = new javax.swing.ButtonGroup();
jRadioButtonScaleLinear = new javax.swing.JRadioButton("Linear", true);
jRadioButtonScaleLinear.setOpaque(false);
jRadioButtonScaleLogarithmic = new javax.swing.JRadioButton("Logarithmic", false);
jRadioButtonScaleLogarithmic.setOpaque(false);
buttonGroupScale.add(jRadioButtonScaleLinear);
buttonGroupScale.add(jRadioButtonScaleLogarithmic);
timeScalePanel.add(jRadioButtonScaleLinear);
timeScalePanel.add(jRadioButtonScaleLogarithmic);
// Update speed (in milleseconds)
JPanel updateSpeedPanel = new JPanel();
updateSpeedPanel.setLayout(new BoxLayout(updateSpeedPanel, BoxLayout.Y_AXIS));
updateSpeedPanel.setBorder(BorderFactory.createTitledBorder("Update Speed"));
//sleepSlider = new javax.swing.JSlider(100,10000,1000);
//sleepSlider.setPaintLabels(true);
//sleepSlider.setPaintTicks(true);
//sleepSlider.setMinorTickSpacing(500);
//sleepSlider.setMajorTickSpacing(2000);
sleepSlider = new LogarithmicTimeJSlider(100, 100000, 1000);
sleepSlider.setPaintTicks(true);
sleepSlider.setPaintLabels(true);
sleepSlider.setMajorTickSpacing(10);
sleepSlider.setMinorTickSpacing(10);
sleepSlider.setToolTipText("Time between updates");
sleepSlider.setOpaque(false);
sleepDelay = new JLabel("Delay: " + sleepSlider.getTime());
sleepSlider.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
sleepSliderPropertyChange(null);
sleepDelay.setText("Delay: " + sleepSlider.getTime());
}
});
updateSpeedPanel.add(sleepSlider);
updateSpeedPanel.add(sleepDelay);
jRadioButtonScaleLinear.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
scaleChanged(evt);
}
});
jRadioButtonScaleLogarithmic.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
scaleChanged(evt);
}
});
this.controlsPanel.add(timeDurationPanel);
this.controlsPanel.add(updateSpeedPanel);
this.controlsPanel.add(timeScalePanel);
return this.controlsPanel;
}
private void scaleChanged(javax.swing.event.ChangeEvent evt) {
setLogarithmic(jRadioButtonScaleLogarithmic.isSelected());
}
private void sleepSliderPropertyChange(java.beans.PropertyChangeEvent evt) {
if (!sleepSlider.getValueIsAdjusting()) {
reschedule();
}
}
/**
* Should be overridden by non-abstract subclasses to provide an observation
* of all time series datasets.
*/
public abstract void addObservation() throws Exception;
private void addObservationHandler() {
try {
addObservation();
if (failures > 0)
failures--;
} catch (Exception e) {
failures++;
// org.openide.windows.IOProvider.getDefault().getStdOut().println(e);
}
if (failures > MAX_FAILURES) {
pauseSchedule();
}
updateRange();
}
protected void createTimeSeries(String name, Object key) {
TimeSeries ts =
new TimeSeries(
name,
Millisecond.class);
//ts.setHistoryCount(1728000); // 48 hours at 100 seconds
ts.setMaximumItemCount(1728000); // 48 hours at 100 seconds
this.timeSeriesMap.put(key, ts);
dataset.addSeries(ts);
}
protected TimeSeries getTimeSeries(Object key) {
return (TimeSeries) this.timeSeriesMap.get(key);
}
/**
* The data generator.
*/
protected class DataGenerator extends TimerTask {
public void run() {
addObservationHandler();
}
}
private static class TestGraph extends AbstractGraphPanel {
public static final String TEST = "test";
private int x = 1;
public TestGraph() {
createTimeSeries(TEST, TEST);
reschedule();
}
public void addObservation() throws Exception {
getTimeSeries(TEST).
add(new Millisecond(), new Integer(x++));
}
}
public static void main(String[] args) {
TestGraph graph = new TestGraph();
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
frame.getContentPane().add(panel);
panel.add(graph, BorderLayout.CENTER);
frame.pack();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
}