Mercurial > hg > release > thermostat-0.9
changeset 65:ad21cb32aad1
Update many charts asynchronously
author | Omair Majid <omajid@redhat.com> |
---|---|
date | Wed, 01 Feb 2012 17:00:45 -0500 |
parents | 41210ed840cf |
children | 1fdd16a78761 |
files | src/com/redhat/thermostat/backend/system/JvmStatVmListener.java src/com/redhat/thermostat/client/HostPanelFacade.java src/com/redhat/thermostat/client/HostPanelFacadeImpl.java src/com/redhat/thermostat/client/VmPanelFacade.java src/com/redhat/thermostat/client/VmPanelFacadeImpl.java src/com/redhat/thermostat/client/strings.properties src/com/redhat/thermostat/client/ui/HostPanel.java src/com/redhat/thermostat/client/ui/RecentTimeSeriesChartController.java src/com/redhat/thermostat/client/ui/RecentTimeSeriesChartPanel.java src/com/redhat/thermostat/client/ui/VmPanel.java |
diffstat | 10 files changed, 585 insertions(+), 106 deletions(-) [+] |
line wrap: on
line diff
--- a/src/com/redhat/thermostat/backend/system/JvmStatVmListener.java Wed Feb 01 15:17:25 2012 -0500 +++ b/src/com/redhat/thermostat/backend/system/JvmStatVmListener.java Wed Feb 01 17:00:45 2012 -0500 @@ -68,6 +68,7 @@ private static final Key vmGcStatTimeStampKey = new Key("timestamp", false); private static final Key vmGcStatCollectorKey = new Key("collector", false); private static final Key vmGcStatRunCountKey = new Key("runtime-count", false); + /** time in microseconds */ private static final Key vmGCstatWallTimeKey = new Key("wall-time", false); private static final Key vmMemoryStatVmIdKey = new Key("vm-id", false);
--- a/src/com/redhat/thermostat/client/HostPanelFacade.java Wed Feb 01 15:17:25 2012 -0500 +++ b/src/com/redhat/thermostat/client/HostPanelFacade.java Wed Feb 01 17:00:45 2012 -0500 @@ -38,8 +38,9 @@ import javax.swing.table.TableModel; +import org.jfree.data.time.TimeSeriesCollection; + public interface HostPanelFacade extends AsyncUiFacade { - public DiscreteTimeData<Double>[] getCpuLoad(); public DiscreteTimeData<Long>[] getMemoryUsage(MemoryType type); @@ -63,4 +64,6 @@ public TableModel getNetworkTableModel(); + public TimeSeriesCollection getCpuLoadDataSet(); + }
--- a/src/com/redhat/thermostat/client/HostPanelFacadeImpl.java Wed Feb 01 15:17:25 2012 -0500 +++ b/src/com/redhat/thermostat/client/HostPanelFacadeImpl.java Wed Feb 01 17:00:45 2012 -0500 @@ -54,6 +54,10 @@ import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; +import org.jfree.data.time.FixedMillisecond; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; + import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; @@ -80,6 +84,10 @@ private final DefaultTableModel networkTableModel = new DefaultTableModel(); private final Vector<String> networkTableColumnVector; + private final TimeSeriesCollection cpuLoadTimeSeriesCollection = new TimeSeriesCollection(); + private final TimeSeries cpuLoadSeries = new TimeSeries("cpu-time"); + private long cpuLoadLastUpdateTime = Long.MIN_VALUE; + private final Timer backgroundUpdateTimer = new Timer(); private Set<MemoryType> toDisplay = new HashSet<MemoryType>(); @@ -98,6 +106,8 @@ networkTableColumnVector.add(_("NETWORK_IPV4_COLUMN")); networkTableColumnVector.add(_("NETWORK_IPV6_COLUMN")); + cpuLoadTimeSeriesCollection.addSeries(cpuLoadSeries); + toDisplay.addAll(Arrays.asList(MemoryType.values())); } @@ -115,8 +125,9 @@ memoryTotal.setText((String) hostInfo.get("memory_total")); doNetworkTableUpdateAsync(); + + doCpuChartUpdateAsync(); } - }, 0, TimeUnit.SECONDS.toMillis(5)); } @@ -202,7 +213,7 @@ String ifaceName = networkIfaceInfo.getInterfaceName(); String ipv4 = networkIfaceInfo.getIp4Addr(); String ipv6 = networkIfaceInfo.getIp6Addr(); - data.add(new Vector<String>(Arrays.asList(new String[] {ifaceName, ipv4, ipv6}))); + data.add(new Vector<String>(Arrays.asList(new String[] { ifaceName, ipv4, ipv6 }))); } facade.networkTableModel.setDataVector(data, facade.networkTableColumnVector); @@ -220,26 +231,11 @@ } @Override - public DiscreteTimeData<Double>[] getCpuLoad() { - List<DiscreteTimeData<Double>> load = new ArrayList<DiscreteTimeData<Double>>(); - DBCursor cursor = cpuStatsCollection.find(new BasicDBObject("agent-id", agent.getAgentId())); - long timestamp = 0; - double data = 0; - while (cursor.hasNext()) { - DBObject stat = cursor.next(); - timestamp = Long.valueOf((String) stat.get("timestamp")); - data = Double.valueOf((String) stat.get("5load")); - load.add(new DiscreteTimeData<Double>(timestamp, data)); - } - // TODO we may also want to avoid sending out thousands of values. - // a subset of values from this entire array should suffice. - return (DiscreteTimeData<Double>[]) load.toArray(new DiscreteTimeData<?>[0]); - } - - @Override public DiscreteTimeData<Long>[] getMemoryUsage(MemoryType type) { List<DiscreteTimeData<Long>> data = new ArrayList<DiscreteTimeData<Long>>(); - DBCursor cursor = memoryStatsCollection.find(new BasicDBObject("agent-id", agent.getAgentId())); + BasicDBObject queryObject = new BasicDBObject(); + queryObject.put("agent-id", agent.getAgentId()); + DBCursor cursor = memoryStatsCollection.find(queryObject); long timestamp = 0; long memoryData = 0; while (cursor.hasNext()) { @@ -276,4 +272,94 @@ } } + @Override + public TimeSeriesCollection getCpuLoadDataSet() { + return cpuLoadTimeSeriesCollection; + } + + private void doCpuChartUpdateAsync() { + CpuLoadChartUpdater updater = new CpuLoadChartUpdater(this); + updater.execute(); + } + + private static class CpuLoadChartUpdater extends SwingWorker<DiscreteTimeData<Double>[], Void> { + + private HostPanelFacadeImpl facade; + + public CpuLoadChartUpdater(HostPanelFacadeImpl impl) { + this.facade = impl; + } + + @Override + protected DiscreteTimeData<Double>[] doInBackground() throws Exception { + return getCpuLoad(facade.cpuLoadLastUpdateTime); + } + + private DiscreteTimeData<Double>[] getCpuLoad(long after) { + List<DiscreteTimeData<Double>> load = new ArrayList<DiscreteTimeData<Double>>(); + BasicDBObject queryObject = new BasicDBObject(); + queryObject.put("agent-id", facade.agent.getAgentId()); + if (after != Long.MIN_VALUE) { + // TODO once we have an index and the 'column' is of type long, use a + // query which can utilize an index. this one doesn't + queryObject.put("$where", "this.timestamp > " + after); + } + DBCursor cursor = facade.cpuStatsCollection.find(queryObject); + long timestamp = 0; + double data = 0; + while (cursor.hasNext()) { + DBObject stat = cursor.next(); + timestamp = Long.valueOf((String) stat.get("timestamp")); + data = Double.valueOf((String) stat.get("5load")); + load.add(new DiscreteTimeData<Double>(timestamp, data)); + } + // TODO we may also want to avoid sending out thousands of values. + // a subset of values from this entire array should suffice. + return (DiscreteTimeData<Double>[]) load.toArray(new DiscreteTimeData<?>[0]); + } + + @Override + protected void done() { + try { + if (facade.cpuLoadLastUpdateTime == Long.MIN_VALUE) { + /* TODO clear stuff? */ + facade.cpuLoadSeries.clear(); + } + + DiscreteTimeData<Double>[] data = get(); + appendCpuChartData(data); + + } catch (ExecutionException ee) { + ee.printStackTrace(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + private void appendCpuChartData(DiscreteTimeData<Double>[] cpuData) { + if (cpuData.length > 0) { + + /* + * We have lots of new data to add. we do it in 2 steps: + * 1. Add everything with notify off. + * 2. Notify the chart that there has been a change. It does + * all the expensive computations and redraws itself. + */ + + DiscreteTimeData<Double> data; + for (int i = 0; i < cpuData.length; i++) { + data = cpuData[i]; + facade.cpuLoadLastUpdateTime = Math.max(facade.cpuLoadLastUpdateTime, data.getTimeInMillis()); + facade.cpuLoadSeries.add( + new FixedMillisecond(data.getTimeInMillis()), data.getData(), + /* notify = */false); + } + + facade.cpuLoadSeries.fireSeriesChanged(); + } + } + + } + + }
--- a/src/com/redhat/thermostat/client/VmPanelFacade.java Wed Feb 01 15:17:25 2012 -0500 +++ b/src/com/redhat/thermostat/client/VmPanelFacade.java Wed Feb 01 17:00:45 2012 -0500 @@ -36,7 +36,9 @@ package com.redhat.thermostat.client; -import com.redhat.thermostat.common.VmMemoryStat; +import org.jfree.data.category.DefaultCategoryDataset; +import org.jfree.data.time.FixedMillisecond; +import org.jfree.data.time.TimeSeriesCollection; /** * Represents information specific to a JVM running on a host somewhere. This is @@ -44,21 +46,6 @@ */ public interface VmPanelFacade extends AsyncUiFacade { - /** - * @return names of the garbage collectors that are operating - */ - public String[] getCollectorNames(); - - /** - * @param collectorName the name of the garbage collector - * @return a list of (time, cumulative time collector has run ) - */ - public DiscreteTimeData<Long>[] getCollectorRunTime(String collectorName); - - public String getCollectorGeneration(String collectorName); - - public VmMemoryStat getLatestMemoryInfo(); - public ChangeableText getVmPid(); public ChangeableText getMainClass(); @@ -77,4 +64,20 @@ public ChangeableText getVmNameAndVersion(); + /** + * @return names of the garbage collectors that are operating + */ + public String[] getCollectorNames(); + + public String getCollectorGeneration(String collectorName); + + /** + * Returns a {@link TimeSeriesCollection} containing TimeSeries for the collector with a + * matching name. The domain is in time ({@link FixedMillisecond}), range is Long, corresponding + * to the time spent collecting in microseconds. + */ + public TimeSeriesCollection getCollectorDataSet(String collectorName); + + public DefaultCategoryDataset getCurrentMemory(); + }
--- a/src/com/redhat/thermostat/client/VmPanelFacadeImpl.java Wed Feb 01 15:17:25 2012 -0500 +++ b/src/com/redhat/thermostat/client/VmPanelFacadeImpl.java Wed Feb 01 17:00:45 2012 -0500 @@ -41,11 +41,21 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import javax.swing.SwingWorker; + +import org.jfree.data.category.DefaultCategoryDataset; +import org.jfree.data.time.FixedMillisecond; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; + import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; @@ -76,6 +86,12 @@ private final ChangeableText vmArguments = new ChangeableText(""); private final ChangeableText vmNameAndVersion = new ChangeableText(""); + private final DefaultCategoryDataset currentMemoryDataset = new DefaultCategoryDataset(); + + private final Map<String, TimeSeriesCollection> collectorSeriesCollection = new HashMap<String, TimeSeriesCollection>(); + private final Map<String, TimeSeries> collectorSeries = new HashMap<String, TimeSeries>(); + private final Map<String, Long> collectorSeriesLastUpdateTime = new HashMap<String, Long>(); + private final Timer timer = new Timer(); private final DateFormat vmRunningTimeFormat; @@ -122,7 +138,32 @@ vmVersion.setText(actualVmVersion); vmArguments.setText((String) vmInfoObject.get("vm-arguments")); vmNameAndVersion.setText(_("VM_INFO_VM_NAME_AND_VERSION", actualVmName, actualVmVersion)); + + String[] collectorNames = getCollectorNames(); + for (String collectorName: collectorNames) { + TimeSeriesCollection seriesCollection = collectorSeriesCollection.get(collectorName); + if (seriesCollection == null) { + seriesCollection = new TimeSeriesCollection(); + collectorSeriesCollection.put(collectorName, seriesCollection); + } + TimeSeries series = collectorSeries.get(collectorName); + if (series == null) { + series = new TimeSeries(collectorName); + collectorSeries.put(collectorName, series); + } + if (seriesCollection.getSeries(collectorName) == null) { + seriesCollection.addSeries(series); + } + if (!collectorSeriesLastUpdateTime.containsKey(collectorName)) { + collectorSeriesLastUpdateTime.put(collectorName, Long.MIN_VALUE); + } + } + + doUpdateCurrentMemoryChartAsync(); + doUpdateCollectorChartsAsync(); + } + }, 0, TimeUnit.SECONDS.toMillis(5)); } @@ -188,24 +229,110 @@ return collectorNames.toArray(new String[0]); } - @Override - public DiscreteTimeData<Long>[] getCollectorRunTime(String collectorName) { - ArrayList<DiscreteTimeData<Long>> result = new ArrayList<DiscreteTimeData<Long>>(); - BasicDBObject queryObject = new BasicDBObject(); - queryObject.put("agent-id", ref.getAgent().getAgentId()); - queryObject.put("vm-id", ref.getId()); - queryObject.put("collector", collectorName); - DBCursor cursor = vmGcStatsCollection.find(queryObject); - long timestamp; - long walltime; - while (cursor.hasNext()) { - DBObject current = cursor.next(); - timestamp = Long.valueOf((String) current.get("timestamp")); - walltime = Long.valueOf((String) current.get("wall-time")); - result.add(new DiscreteTimeData<Long>(timestamp, walltime)); + private void doUpdateCollectorChartsAsync() { + String[] collectorNames = getCollectorNames(); + for (String name: collectorNames) { + CollectorChartUpdater updater = new CollectorChartUpdater(this, name); + updater.execute(); + } + } + + public static class CollectorChartUpdater extends SwingWorker<DiscreteTimeData<Double>[], Void> { + + private VmPanelFacadeImpl facade; + private String collectorName; + + public CollectorChartUpdater(VmPanelFacadeImpl facade, String collectorName) { + this.facade = facade; + this.collectorName = collectorName; + } + + @Override + protected DiscreteTimeData<Double>[] doInBackground() throws Exception { + Long after = facade.collectorSeriesLastUpdateTime.get(collectorName); + if (after == null) { + after = Long.MIN_VALUE; + } + return getCollectorRunTime(after); + } + + private DiscreteTimeData<Double>[] getCollectorRunTime(long after) { + ArrayList<DiscreteTimeData<Double>> result = new ArrayList<DiscreteTimeData<Double>>(); + BasicDBObject queryObject = new BasicDBObject(); + queryObject.put("agent-id", facade.ref.getAgent().getAgentId()); + queryObject.put("vm-id", facade.ref.getId()); + if (after != Long.MIN_VALUE) { + // TODO once we have an index and the 'column' is of type long, use a + // query which can utilize an index. this one doesn't + queryObject.put("$where", "this.timestamp > " + after); + } + queryObject.put("collector", collectorName); + DBCursor cursor = facade.vmGcStatsCollection.find(queryObject); + long timestamp; + double walltime; + while (cursor.hasNext()) { + DBObject current = cursor.next(); + timestamp = Long.valueOf((String) current.get("timestamp")); + // convert microseconds to seconds + walltime = 1.0E-6 * Long.valueOf((String) current.get("wall-time")); + result.add(new DiscreteTimeData<Double>(timestamp, walltime)); + } + + return (DiscreteTimeData<Double>[]) result.toArray(new DiscreteTimeData<?>[0]); } - return (DiscreteTimeData<Long>[]) result.toArray(new DiscreteTimeData<?>[0]); + @Override + protected void done() { + try { + Long after = facade.collectorSeriesLastUpdateTime.get(collectorName); + if (after == null) { + after = Long.MIN_VALUE; + } + after = appendCollectorDataToChart(get(), facade.collectorSeries.get(collectorName), after); + facade.collectorSeriesLastUpdateTime.put(collectorName, after); + } catch (ExecutionException ee) { + ee.printStackTrace(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + private long appendCollectorDataToChart(DiscreteTimeData<Double>[] collectorData, TimeSeries collectorSeries, long prevMaxTime) { + long maxTime = prevMaxTime; + + if (collectorData.length > 0) { + + /* + * We have lots of new data to add. we do it in 2 steps: + * 1. Add everything with notify off. + * 2. Notify the chart that there has been a change. It + * does all the expensive computations and redraws itself. + */ + + DiscreteTimeData<Double> data; + for (int i = 0; i < collectorData.length; i++) { + data = collectorData[i]; + maxTime = Math.max(maxTime, data.getTimeInMillis()); + collectorSeries.add( + new FixedMillisecond(data.getTimeInMillis()), data.getData(), + /* notify = */false); + } + + collectorSeries.fireSeriesChanged(); + } + + return maxTime; + } + } + + @Override + public TimeSeriesCollection getCollectorDataSet(String collectorName) { + TimeSeriesCollection seriesCollection = collectorSeriesCollection.get(collectorName); + if (seriesCollection == null) { + seriesCollection = new TimeSeriesCollection(); + collectorSeriesCollection.put(collectorName, seriesCollection); + } + return seriesCollection; } @Override @@ -221,8 +348,48 @@ return _("UNKNOWN_GEN"); } - @Override - public VmMemoryStat getLatestMemoryInfo() { + private void doUpdateCurrentMemoryChartAsync() { + UpdateCurrentMemory worker = new UpdateCurrentMemory(this); + worker.execute(); + } + + private static class UpdateCurrentMemory extends SwingWorker<VmMemoryStat, Void> { + + private final VmPanelFacadeImpl facade; + + public UpdateCurrentMemory(VmPanelFacadeImpl facade) { + this.facade = facade; + } + + @Override + protected VmMemoryStat doInBackground() throws Exception { + return facade.getLatestMemoryInfo(); + } + + @Override + protected void done() { + try { + VmMemoryStat info = get(); + DefaultCategoryDataset dataset = facade.currentMemoryDataset; + List<Generation> generations = info.getGenerations(); + for (Generation generation: generations) { + List<Space> spaces = generation.spaces; + for (Space space: spaces) { + dataset.addValue(space.used, _("VM_CURRENT_MEMORY_CHART_USED"), space.name); + dataset.addValue(space.capacity - space.used, _("VM_CURRENT_MEMORY_CHART_CAPACITY"), space.name); + dataset.addValue(space.maxCapacity - space.capacity, _("VM_CURRENT_MEMORY_CHART_MAX_CAPACITY"), space.name); + } + } + } catch (InterruptedException ie) { + ie.printStackTrace(); + } catch (ExecutionException ee) { + ee.printStackTrace(); + } + } + + } + + private VmMemoryStat getLatestMemoryInfo() { BasicDBObject query = new BasicDBObject(); query.put("agent-id", ref.getAgent().getAgentId()); query.put("vm-id", ref.getId()); @@ -267,7 +434,7 @@ space.name = spaceName; space.capacity = Long.valueOf((String)info.get("capacity")); space.maxCapacity = Long.valueOf((String)info.get("max-capacity")); - space.used = Long.valueOf((String)info.get("used")); + space.used = Long.valueOf((String) info.get("used")); if (target.spaces == null) { target.spaces = new ArrayList<Space>(); } @@ -278,4 +445,9 @@ return cached; } + @Override + public DefaultCategoryDataset getCurrentMemory() { + return currentMemoryDataset; + } + }
--- a/src/com/redhat/thermostat/client/strings.properties Wed Feb 01 15:17:25 2012 -0500 +++ b/src/com/redhat/thermostat/client/strings.properties Wed Feb 01 17:00:45 2012 -0500 @@ -30,6 +30,11 @@ UNKNOWN_GEN = Unknown SOME_GENERATION = {0} Generation +SECONDS = Seconds +MINUTES = Minutes +HOURS = Hours +DAYS = Days + STARTUP_MODE_SELECTION_DIALOG_TITLE = Welcome to Thermostat! STARTUP_MODE_SELECTION_INTRO = Which JVMs to do you want to monitor? STARTUP_MODE_SELECTION_TYPE_LOCAL = Local @@ -75,9 +80,8 @@ NETWORK_IPV6_COLUMN = IPv6 Address HOST_CPU_SECTION_OVERVIEW = Processor -HOST_CPU_USAGE_CHART_TITLE = Cpu Usage HOST_CPU_USAGE_CHART_TIME_LABEL = Time -HOST_CPU_USAGE_CHART_VALUE_LABEL = Usage +HOST_CPU_USAGE_CHART_VALUE_LABEL = Avg Load HOST_MEMORY_SECTION_OVERVIEW = Memory HOST_MEMORY_CHART_TITLE = Memory @@ -119,4 +123,4 @@ VM_GC_COLLECTOR_OVER_GENERATION = Collector {0} running on {1} VM_GC_COLLECTOR_CHART_REAL_TIME_LABEL = Time -VM_GC_COLLECTOR_CHART_GC_TIME_LABEL = Total Time Spent on GC +VM_GC_COLLECTOR_CHART_GC_TIME_LABEL = Total Time Spent on GC (s)
--- a/src/com/redhat/thermostat/client/ui/HostPanel.java Wed Feb 01 15:17:25 2012 -0500 +++ b/src/com/redhat/thermostat/client/ui/HostPanel.java Wed Feb 01 17:00:45 2012 -0500 @@ -56,8 +56,6 @@ import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; -import org.jfree.data.time.FixedMillisecond; -import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; @@ -174,26 +172,15 @@ table.setBorder(Components.smallBorder()); contentArea.add(table, c); - DiscreteTimeData<Double>[] cpuData = facade.getCpuLoad(); - TimeSeries series = new TimeSeries("cpu-load"); - for (DiscreteTimeData<Double> data : cpuData) { - series.add(new FixedMillisecond(data.getTimeInMillis()), data.getData()); - } - TimeSeriesCollection dataset = new TimeSeriesCollection(); - dataset.addSeries(series); + TimeSeriesCollection dataset = facade.getCpuLoadDataSet(); JFreeChart chart = ChartFactory.createTimeSeriesChart( - _("HOST_CPU_USAGE_CHART_TITLE"), + null, _("HOST_CPU_USAGE_CHART_TIME_LABEL"), _("HOST_CPU_USAGE_CHART_VALUE_LABEL"), dataset, false, false, false); - ChartPanel chartPanel = new ChartPanel(chart); - // make this chart non-interactive - chartPanel.setDisplayToolTips(true); - chartPanel.setDoubleBuffered(true); - chartPanel.setMouseZoomable(false); - chartPanel.setPopupMenu(null); + JPanel chartPanel = new RecentTimeSeriesChartPanel(new RecentTimeSeriesChartController(chart)); c.gridy++; c.fill = GridBagConstraints.BOTH;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/RecentTimeSeriesChartController.java Wed Feb 01 17:00:45 2012 -0500 @@ -0,0 +1,99 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.ui; + +import java.util.concurrent.TimeUnit; + +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.XYPlot; + +public class RecentTimeSeriesChartController { + + private final int DEFAULT_VALUE = 10; + private final TimeUnit DEFAULT_UNIT = TimeUnit.MINUTES; + + private JFreeChart chart; + private ChartPanel panel; + private int timeValue = DEFAULT_VALUE; + private TimeUnit timeUnit = DEFAULT_UNIT; + + public RecentTimeSeriesChartController(JFreeChart chart) { + this.chart = chart; + this.panel = new ChartPanel(chart); + + // instead of just disabling display of tooltips, disable their generation too + if (chart.getPlot() instanceof XYPlot) { + chart.getXYPlot().getRenderer().setBaseToolTipGenerator(null); + } + + chart.getXYPlot().getDomainAxis().setAutoRange(true); + chart.getXYPlot().getDomainAxis().setFixedAutoRange(timeUnit.toMillis(timeValue)); + + chart.getXYPlot().getRangeAxis().setAutoRange(true); + + } + + public ChartPanel getChartPanel() { + return panel; + } + + public TimeUnit[] getTimeUnits() { + return new TimeUnit[] { TimeUnit.DAYS, TimeUnit.HOURS, TimeUnit.MINUTES }; + } + + public int getTimeValue() { + return timeValue; + } + + public TimeUnit getTimeUnit() { + return timeUnit; + } + + public void setTime(int value, TimeUnit unit) { + this.timeValue = value; + this.timeUnit = unit; + + updateChart(); + } + + private void updateChart() { + chart.getXYPlot().getDomainAxis().setAutoRange(true); + chart.getXYPlot().getDomainAxis().setFixedAutoRange(timeUnit.toMillis(timeValue)); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/RecentTimeSeriesChartPanel.java Wed Feb 01 17:00:45 2012 -0500 @@ -0,0 +1,152 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.ui; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.concurrent.TimeUnit; + +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; + +import org.jfree.chart.ChartPanel; + +public class RecentTimeSeriesChartPanel extends JPanel { + + private static final long serialVersionUID = -1733906800911900456L; + + private final RecentTimeSeriesChartController controller; + + public RecentTimeSeriesChartPanel(RecentTimeSeriesChartController controller) { + this.controller = controller; + + this.setLayout(new BorderLayout()); + + ChartPanel cp = controller.getChartPanel(); + + cp.setDisplayToolTips(false); + cp.setDoubleBuffered(true); + cp.setMouseZoomable(false); + cp.setPopupMenu(null); + + add(cp); + add(getChartControls(), BorderLayout.SOUTH); + } + + private Component getChartControls() { + JPanel container = new JPanel(); + + final JTextField durationSelector = new JTextField(5); + final JComboBox unitSelector = new JComboBox(controller.getTimeUnits()); + + int defaultValue = controller.getTimeValue(); + TimeUnit defaultUnit = controller.getTimeUnit(); + + TimeUnitChangeListener timeUnitChangeListener = new TimeUnitChangeListener(controller, defaultValue, defaultUnit); + + durationSelector.getDocument().addDocumentListener(timeUnitChangeListener); + unitSelector.addActionListener(timeUnitChangeListener); + + durationSelector.setText(String.valueOf(defaultValue)); + unitSelector.setSelectedItem(defaultUnit); + + container.add(new JLabel("Display the most recent")); + container.add(durationSelector); + container.add(unitSelector); + + return container; + } + + private static class TimeUnitChangeListener implements DocumentListener, ActionListener { + + private final RecentTimeSeriesChartController controller; + private int value; + private TimeUnit unit; + + public TimeUnitChangeListener(RecentTimeSeriesChartController controller, int defaultValue, TimeUnit defaultUnit) { + this.controller = controller; + this.value = defaultValue; + this.unit = defaultUnit; + } + + @Override + public void removeUpdate(DocumentEvent event) { + changed(event.getDocument()); + } + + @Override + public void insertUpdate(DocumentEvent event) { + changed(event.getDocument()); + } + + @Override + public void changedUpdate(DocumentEvent event) { + changed(event.getDocument()); + } + + private void changed(Document doc) { + try { + this.value = Integer.valueOf(doc.getText(0, doc.getLength())); + } catch (NumberFormatException nfe) { + // ignore + } catch (BadLocationException ble) { + // ignore + } + updateChartParameters(); + } + + private void updateChartParameters() { + controller.setTime(value, unit); + } + + @Override + public void actionPerformed(ActionEvent e) { + JComboBox comboBox = (JComboBox) e.getSource(); + TimeUnit time = (TimeUnit) comboBox.getSelectedItem(); + this.unit = time; + updateChartParameters(); + } + } +}
--- a/src/com/redhat/thermostat/client/ui/VmPanel.java Wed Feb 01 15:17:25 2012 -0500 +++ b/src/com/redhat/thermostat/client/ui/VmPanel.java Wed Feb 01 17:00:45 2012 -0500 @@ -53,17 +53,11 @@ import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.PlotOrientation; import org.jfree.data.category.DefaultCategoryDataset; -import org.jfree.data.time.FixedMillisecond; -import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; -import com.redhat.thermostat.client.DiscreteTimeData; import com.redhat.thermostat.client.VmPanelFacade; import com.redhat.thermostat.client.ui.SimpleTable.Section; import com.redhat.thermostat.client.ui.SimpleTable.TableEntry; -import com.redhat.thermostat.common.VmMemoryStat; -import com.redhat.thermostat.common.VmMemoryStat.Generation; -import com.redhat.thermostat.common.VmMemoryStat.Space; public class VmPanel extends JPanel { @@ -147,18 +141,7 @@ } private Component createCurrentMemoryDisplay() { - DefaultCategoryDataset data = new DefaultCategoryDataset(); - - VmMemoryStat info = facade.getLatestMemoryInfo(); - List<Generation> generations = info.getGenerations(); - for (Generation generation : generations) { - List<Space> spaces = generation.spaces; - for (Space space : spaces) { - data.addValue(space.used, _("VM_CURRENT_MEMORY_CHART_USED"), space.name); - data.addValue(space.capacity - space.used, _("VM_CURRENT_MEMORY_CHART_CAPACITY"), space.name); - data.addValue(space.maxCapacity - space.capacity, _("VM_CURRENT_MEMORY_CHART_MAX_CAPACITY"), space.name); - } - } + DefaultCategoryDataset data = facade.getCurrentMemory(); JFreeChart chart = ChartFactory.createStackedBarChart( null, @@ -215,13 +198,7 @@ detailsPanel.add(Components.header(_("VM_GC_COLLECTOR_OVER_GENERATION", collectorName, facade.getCollectorGeneration(collectorName))), BorderLayout.NORTH); - DiscreteTimeData<Long>[] cpuData = facade.getCollectorRunTime(collectorName); - TimeSeries series = new TimeSeries("gc-runs"); - for (DiscreteTimeData<Long> data : cpuData) { - series.add(new FixedMillisecond(data.getTimeInMillis()), data.getData()); - } - TimeSeriesCollection dataset = new TimeSeriesCollection(); - dataset.addSeries(series); + TimeSeriesCollection dataset = facade.getCollectorDataSet(collectorName); JFreeChart chart = ChartFactory.createTimeSeriesChart( null, _("VM_GC_COLLECTOR_CHART_REAL_TIME_LABEL"), @@ -229,12 +206,7 @@ dataset, false, false, false); - ChartPanel chartPanel = new ChartPanel(chart); - // make this chart non-interactive - chartPanel.setDisplayToolTips(true); - chartPanel.setDoubleBuffered(true); - chartPanel.setMouseZoomable(false); - chartPanel.setPopupMenu(null); + JPanel chartPanel = new RecentTimeSeriesChartPanel(new RecentTimeSeriesChartController(chart)); detailsPanel.add(chartPanel, BorderLayout.CENTER);