/*
* Copyright (c) 2012 European Synchrotron Radiation Facility,
* Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
/*
* Program to display JVM memory usage in a JLChart.
* Created on February 1, 2009
* By Kenneth Evans, Jr.
*/
package fable.framework.toolbox;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import fr.esrf.tangoatk.widget.util.chart.DataList;
import fr.esrf.tangoatk.widget.util.chart.IJLChartActionListener;
import fr.esrf.tangoatk.widget.util.chart.JLAxis;
import fr.esrf.tangoatk.widget.util.chart.JLChart;
import fr.esrf.tangoatk.widget.util.chart.JLChartActionEvent;
import fr.esrf.tangoatk.widget.util.chart.JLDataView;
/**
* This class implements a JLChart showing JVM memory usage.
*/
public class JLChartMemoryUsage extends JPanel {
private static final boolean verbose = false;
private static final boolean debug = false;
private static final long serialVersionUID = 1L;
/**
* Scale to MBytes.
*/
private static final double SCALE = 1. / 1048576.;
public static final int DEFAULT_INTERVAL = 1000;
public static final int DEFAULT_AGE = 60000;
public static final boolean DEFAULT_SHOW_MAX = true;
public static final boolean DEFAULT_SHOW_LEGEND = true;
private IJLChartActionListener chartActionListener;
private boolean showMax = DEFAULT_SHOW_MAX;
private boolean showLegend = DEFAULT_SHOW_LEGEND;
private JLChart chart = null;
private JLDataView total;
private JLDataView used;
private JLDataView max;
private Timer timer = null;
private int interval = DEFAULT_INTERVAL;
private int maxAge = DEFAULT_AGE;
private String header = null;
// private JCheckBoxMenuItem showLegendMenuItem = null;
/**
* JLChartMemoryUsage constructor
*/
public JLChartMemoryUsage() {
this(DEFAULT_AGE);
}
/**
* JLChartMemoryUsage constructor
*
* @param maxAge
* The maximum age (in milliseconds).
*/
public JLChartMemoryUsage(int maxAge) {
super(new BorderLayout());
this.maxAge = maxAge;
chart = new JLChart();
chart.setHeader(getName());
chart.setHeaderFont(new Font("Dialog", Font.BOLD, 18));
chart.setDisplayDuration(maxAge);
chart.getXAxis().setName("Time");
chart.getXAxis().setAutoScale(true);
chart.getXAxis().setGridVisible(true);
chart.getXAxis().setSubGridVisible(true);
chart.getXAxis().setAnnotation(JLAxis.TIME_ANNO);
chart.getXAxis().setAxisDuration(maxAge);
chart.getXAxis().setFitXAxisToDisplayDuration(true);
chart.getY1Axis().setName("Memory, MB");
chart.getY1Axis().setAutoScale(true);
chart.getY1Axis().setGridVisible(true);
chart.getY1Axis().setSubGridVisible(true);
total = new JLDataView();
total.setColor(new Color(0, 0, 255));
total.setName("Total Memory");
used = new JLDataView();
used.setColor(new Color(0, 0, 0));
used.setName("Used Memory");
max = new JLDataView();
max.setColor(new Color(255, 0, 0));
max.setName("Max Memory");
chart.getY1Axis().addDataView(total);
chart.getY1Axis().addDataView(used);
chart.getY1Axis().addDataView(max);
// Modify the context menu
extendPopupMenu();
add(chart, BorderLayout.CENTER);
}
/**
* Returns a String with configuration information.
*
* @return
*/
public String getConfiguration() {
String ls = System.getProperty("line.separator");
String info = "";
info += "Chart Configuration" + ls;
info += chart.getConfiguration() + ls;
JLAxis axis = chart.getXAxis();
info += "X Axis: " + axis.getAxeName() + ls;
info += axis.getConfiguration(" ") + ls;
info += " JLDataViews:" + ls;
Vector<JLDataView> views = axis.getViews();
int i = 0;
for (JLDataView dv : views) {
info += " DataView " + i + " " + dv.getName() + ls;
info += dv.getConfiguration(" ") + ls;
}
axis = chart.getY1Axis();
info += "Y1 Axis: " + axis.getAxeName() + ls;
info += axis.getConfiguration(" ") + ls;
info += " JLDataViews:" + ls;
views = axis.getViews();
i = 0;
for (JLDataView dv : views) {
info += " DataView " + i + " " + dv.getName() + ls;
info += dv.getConfiguration(" ") + ls;
}
return info;
}
/**
* Extends the JLChart context menu.
*/
private void extendPopupMenu() {
if (chart == null)
return;
// There is already a separator before the added items
// chart.addSeparator();
// Remove standard items
if (true) {
chart.removeMenuItem(JLChart.MENU_TABLE);
chart.removeMenuItem(JLChart.MENU_STAT);
chart.removeMenuItem(JLChart.MENU_DATASAVE);
// No variable for abscissa margin, save snapshot
}
// Add new items
chart.addUserAction("Toggle Max Memory");
chart.addUserAction("Toggle Legend");
chart.addUserAction("Set Max Age...");
chartActionListener = new IJLChartActionListener() {
private static final long serialVersionUID = 1L;
public void actionPerformed(JLChartActionEvent evt) {
// DEBUG
// if (false) {
// System.out.println("actionPerformed");
// System.out.println(evt);
// System.out.println(evt.getName());
// System.out.println(evt.getState());
// }
if (evt.getName().equals("Toggle Max Memory")) {
toggleMax();
} else if (evt.getName().equals("Toggle Legend")) {
chart.setLabelVisible(!chart.isLabelVisible());
} else if (evt.getName().equals("Set Max Age...")) {
queryResetMaxAge();
}
}
public boolean getActionState(JLChartActionEvent evt) {
// DEBUG
// if (false) {
// System.out.println("getActionState");
// System.out.println(evt);
// System.out.println(evt.getName());
// System.out.println(evt.getState());
// }
// Only used with check boxes
return false;
}
};
chart.addJLChartActionListener(chartActionListener);
}
/**
* The update method for the timer.
*/
public void update() {
// Max memory is the maximum allowed before getting out-of-memory errors
// Total memory is what is currently allocated
// Free memory is what is not used of what is allocated
// Used is total - free
double totalMem = SCALE * Runtime.getRuntime().totalMemory();
double freeMem = SCALE * Runtime.getRuntime().freeMemory();
double maxMem = SCALE * Runtime.getRuntime().maxMemory();
long time = System.currentTimeMillis();
chart.addData(total, time, totalMem);
chart.addData(used, time, totalMem - freeMem);
chart.addData(max, time, maxMem);
if (verbose) {
System.out.println("Update: time: " + time);
System.out.println("length=" + total.getDataLength() + " maxTime="
+ total.getMaxTime());
DataList last = total.getLastValue();
if (last != null) {
// last seems to always be null
System.out.println("lastValue=" + last.x + " " + last.y);
}
if (false) {
// Print the data array
int i = 0;
DataList data = total.getData();
while (data != null) {
System.out.println(i++ + " data: " + data.x + " " + data.y);
data = data.next;
}
}
}
}
/**
* Bring up a dialog to set the interval.
*/
public void queryResetInterval() {
String result = JOptionPane.showInputDialog(
"Enter update interval in ms", Integer.toString(interval));
if (result != null) {
int newVal = 0;
try {
newVal = Integer.valueOf(result).intValue();
} catch (NumberFormatException ex) {
SWTUtils.errMsgAsync("MemoryUsage: Invalid interval");
return;
}
setInterval(newVal);
}
}
/**
* Bring up a dialog to set the maxAge.
*/
public void queryResetMaxAge() {
String result = JOptionPane.showInputDialog("Enter maximum age in ms",
Integer.toString(maxAge));
if (result != null) {
int newVal = 0;
try {
newVal = Integer.valueOf(result).intValue();
} catch (NumberFormatException ex) {
SWTUtils.errMsgAsync("MemoryUsage: Invalid age");
return;
}
setMaxAge(newVal);
}
}
/**
* Toggle whether the maximum value is shown.
*/
public void toggleMax() {
if (max == null)
return;
if (showMax) {
showMax = false;
chart.getY1Axis().removeDataView(max);
} else {
showMax = true;
chart.getY1Axis().addDataView(max);
}
}
/**
* Starts the updating, stopping any previous updating first. Note that the
* timer may continue to run for a long time after references to it are
* gone. See the documentation for java.util.Timer (even though this class
* uses javax.swing.Timer).
*/
public void start() {
stop();
timer = new Timer(interval, new ActionListener() {
public void actionPerformed(ActionEvent event) {
update();
}
});
timer.start();
}
/**
* Stops the updating.
*/
public void stop() {
if (timer != null) {
timer.stop();
timer = null;
}
}
/**
* Removes references. Use this to allow it to be garbage collected.
*/
public void finish() {
// TODO
if (chart != null && chartActionListener != null) {
chart.removeJLChartActionListener(chartActionListener);
}
chart = null;
chartActionListener = null;
}
// Getters and setters
/**
* @return The value of interval in ms.
*/
public int getInterval() {
return interval;
}
/**
* @param interval
* The new value for interval in ms.
*/
public void setInterval(int interval) {
if (interval == this.interval)
return;
this.interval = interval;
// Restart the timer if it is running, otherwise do nothing
if (timer != null && timer.isRunning()) {
start();
}
}
/**
* @return The value of maxAge in ms.
*/
public int getMaxAge() {
return maxAge;
}
/**
* @param maxAge
* The new value for maxAge in ms.
*/
public void setMaxAge(int maxAge) {
if (maxAge == this.maxAge)
return;
this.maxAge = maxAge;
chart.setDisplayDuration(maxAge);
}
/**
* @return the showLegend
*/
public boolean isShowLegend() {
return showLegend;
}
/**
* @param showLegend
* the showLegend to set
*/
public void setShowLegend(boolean showLegend) {
this.showLegend = showLegend;
}
/**
* @return The value of header.
*/
public String getHeader() {
return header;
}
/**
* @param header
* The new value for header.
*/
public void setHeader(String header) {
if (header == this.header)
return;
this.header = header;
if (chart != null)
chart.setHeader(header);
}
/**
* @return The value of chart.
*/
public JLChart getChart() {
return chart;
}
/**
* @return the showMax
*/
public boolean getShowMax() {
return showMax;
}
/**
* @param showMax
* the showMax to set
*/
public void setShowMax(boolean showMax) {
this.showMax = showMax;
}
/**
* Entry point for the sample application.
*
* @param args
* ignored.
*/
public static void main(String[] args) {
try {
// Set window decorations
JFrame.setDefaultLookAndFeelDecorated(true);
// Set the native look and feel
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
// Make the job run in the AWT thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JLChartMemoryUsage panel = new JLChartMemoryUsage(600000);
// panel.setInterval(100); // For testing
panel.setInterval(1000);
panel.setHeader("JVM Memory Usage");
// panel.setDoMax(false);
panel.start();
if (debug) {
System.out.println(panel.getConfiguration());
}
JFrame frame = new JFrame("JLChart Memory Usage");
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.setBounds(200, 120, 600, 280);
// EXIT_ON_CLOSE is necessary to stop the timer
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
} catch (Throwable t) {
t.printStackTrace();
}
}
}