Mercurial > hg > release > thermostat-0.4
changeset 328:8dff701e098a
Clarify GC charts
Part of the fix for PR960.
Reviewed-by: vanaltj
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-May/001198.html
author | Omair Majid <omajid@redhat.com> |
---|---|
date | Wed, 23 May 2012 12:41:57 -0400 |
parents | 65ffb89c766b |
children | e6b1697ab4d2 |
files | client/core/src/main/java/com/redhat/thermostat/client/ui/SampledDataset.java client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcController.java client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcPanel.java client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcView.java client/core/src/main/resources/com/redhat/thermostat/client/locale/strings.properties client/src/main/java/com/redhat/thermostat/client/ui/SampledDataset.java client/src/test/java/com/redhat/thermostat/client/ui/VmGcControllerTest.java common/src/main/java/com/redhat/thermostat/common/model/IntervalTimeData.java distribution/config/osgi-export.properties |
diffstat | 8 files changed, 517 insertions(+), 39 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/SampledDataset.java Wed May 23 12:41:57 2012 -0400 @@ -0,0 +1,194 @@ +/* + * 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 org.jfree.data.xy.AbstractXYDataset; +import org.jfree.data.xy.IntervalXYDataset; + +/** + * Represents a value sampled between two points in time. + */ +public class SampledDataset extends AbstractXYDataset implements IntervalXYDataset { + + private static final String SERIES_KEY = "0"; + private static final int DEFAULT_SIZE = 32; + + private long[] xStart = new long[DEFAULT_SIZE]; + private long[] xEnd = new long[DEFAULT_SIZE]; + private double[] value = new double[DEFAULT_SIZE]; + + private int count = 0; + + public void add(long startX, long endX, double value) { + if (xStart.length == count) { + long[] newXStart = new long[xStart.length * 2]; + System.arraycopy(xStart, 0, newXStart, 0, xStart.length); + xStart = newXStart; + long[] newXEnd = new long[xEnd.length * 2]; + System.arraycopy(xEnd, 0, newXEnd, 0, xEnd.length); + xEnd = newXEnd; + double[] newValue = new double[this.value.length * 2]; + System.arraycopy(this.value, 0, newValue, 0, this.value.length); + this.value = newValue; + } + xStart[count] = startX; + xEnd[count] = endX; + this.value[count] = value; + count++; + } + + public void clear() { + count = 0; + fireDatasetChanged(); + } + + @Override + public int getItemCount(int series) { + checkSeries(series); + return count; + } + + @Override + public Number getX(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return xStart[item]; + } + + @Override + public Number getY(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return value[item]; + } + + @Override + public Number getStartX(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return xStart[item]; + } + + @Override + public double getStartXValue(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return xStart[item]; + } + + @Override + public Number getEndX(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return xEnd[item]; + } + + @Override + public double getEndXValue(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return xEnd[item]; + } + + @Override + public Number getStartY(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return value[item]; + } + + + @Override + public double getStartYValue(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return value[item]; + } + + @Override + public Number getEndY(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return value[item]; + } + + @Override + public double getEndYValue(int series, int item) { + checkSeries(series); + checkItemIndex(item); + return value[item]; + } + + @Override + public int getSeriesCount() { + return 1; + } + + @Override + public Comparable<String> getSeriesKey(int series) { + checkSeries(series); + return SERIES_KEY; + } + + @Override + public int indexOf(Comparable seriesKey) { + if (seriesKey == null) { + return -1; + } + if (seriesKey.compareTo(SERIES_KEY) == 0) { + return 0; + } + return -1; + } + + private void checkSeries(int series) { + if (series != 0) { + throw new IllegalArgumentException(this.getClass().getName() + " supports only 1 series"); + } + } + + private void checkItemIndex(int index) { + if (index >= (count)) { + throw new IllegalArgumentException(this.getClass().getName() + " supports only 1 series"); + } + } + + public void fireSeriesChanged() { + fireDatasetChanged(); + } + +}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcController.java Wed May 23 12:03:47 2012 -0400 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcController.java Wed May 23 12:41:57 2012 -0400 @@ -40,10 +40,13 @@ import java.awt.Component; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.TimeUnit; @@ -59,7 +62,7 @@ import com.redhat.thermostat.common.dao.VmGcStatDAO; import com.redhat.thermostat.common.dao.VmMemoryStatDAO; import com.redhat.thermostat.common.dao.VmRef; -import com.redhat.thermostat.common.model.DiscreteTimeData; +import com.redhat.thermostat.common.model.IntervalTimeData; import com.redhat.thermostat.common.model.VmGcStat; import com.redhat.thermostat.common.model.VmMemoryStat; import com.redhat.thermostat.common.model.VmMemoryStat.Generation; @@ -72,10 +75,19 @@ private final VmGcStatDAO gcDao; private final VmMemoryStatDAO memDao; - private final Set<String> addedCollectors = new TreeSet<String>(); + private final Set<String> addedCollectors = new TreeSet<>(); + // the last value seen for each collector + private final Map<String, VmGcStat> lastValueSeen = new TreeMap<>(); private final Timer timer; + private final Comparator<VmGcStat> vmGcStatComparator = new Comparator<VmGcStat>() { + @Override + public int compare(VmGcStat o1, VmGcStat o2) { + return Long.compare(o1.getTimeStamp(), o2.getTimeStamp()); + } + }; + public VmGcController(VmRef ref) { this.ref = ref; this.view = ApplicationContext.getInstance().getViewFactory().getView(VmGcView.class); @@ -104,7 +116,12 @@ timer.setAction(new Runnable() { @Override public void run() { - doUpdateCollectorData(); + try { + doUpdateCollectorData(); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } } }); timer.setSchedulingType(SchedulingType.FIXED_RATE); @@ -128,21 +145,38 @@ } private void doUpdateCollectorData() { - Map<String, List<DiscreteTimeData<? extends Number>>> dataToAdd = new HashMap<>(); - for (VmGcStat stat : gcDao.getLatestVmGcStats(ref)) { - double walltime = 1.0E-6 * stat.getWallTime(); + Map<String, List<IntervalTimeData<Double>>> dataToAdd = new HashMap<>(); + List<VmGcStat> sortedList = gcDao.getLatestVmGcStats(ref); + Collections.sort(sortedList, vmGcStatComparator); + + for (VmGcStat stat : sortedList) { String collector = stat.getCollectorName(); - List<DiscreteTimeData<? extends Number>> data = dataToAdd.get(collector); + List<IntervalTimeData<Double>> data = dataToAdd.get(collector); if (data == null) { - data = new ArrayList<DiscreteTimeData<? extends Number>>(); + data = new ArrayList<>(); dataToAdd.put(collector, data); } - data.add(new DiscreteTimeData<Double>(stat.getTimeStamp(), walltime)); + if (lastValueSeen.containsKey(collector)) { + if (stat.getTimeStamp() <= lastValueSeen.get(collector).getTimeStamp()) { + System.out.println("new gc collector value is older than previous value"); + } + VmGcStat last = lastValueSeen.get(collector); + long diffInMicro = (stat.getWallTime() - last.getWallTime()); + double diffInMillis = diffInMicro / 1000.0; + // TODO there is not much point in adding data when diff is 0, + // but we need to make the chart scroll automatically based on + // the current time when we do that + // if (diff != 0) { + data.add(new IntervalTimeData<>(last.getTimeStamp(), stat.getTimeStamp(), diffInMillis)); + // } + } + lastValueSeen.put(collector, stat); + } - for (Map.Entry<String, List<DiscreteTimeData<? extends Number>>> entry : dataToAdd.entrySet()) { + for (Map.Entry<String, List<IntervalTimeData<Double>>> entry : dataToAdd.entrySet()) { String name = entry.getKey(); if (!addedCollectors.contains(name)) { - view.addChart(name, chartName(name, getCollectorGeneration(name))); + view.addChart(name, chartName(name, getCollectorGeneration(name)), "ms"); addedCollectors.add(name); } view.addData(entry.getKey(), entry.getValue()); @@ -161,7 +195,7 @@ } /** - * @return + * @return the {@link Component} representing the actual view of this controller */ public Component getComponent() { return view.getUiComponent();
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcPanel.java Wed May 23 12:03:47 2012 -0400 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcPanel.java Wed May 23 12:41:57 2012 -0400 @@ -52,14 +52,18 @@ import org.jfree.chart.ChartFactory; 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.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.renderer.xy.StandardXYBarPainter; +import org.jfree.chart.renderer.xy.XYBarRenderer; +import org.jfree.data.RangeType; +import org.jfree.data.xy.IntervalXYDataset; import com.redhat.thermostat.client.locale.LocaleResources; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; -import com.redhat.thermostat.common.model.DiscreteTimeData; +import com.redhat.thermostat.common.model.IntervalTimeData; public class VmGcPanel extends JPanel implements VmGcView { @@ -67,7 +71,7 @@ private final ActionNotifier<Action> notifier = new ActionNotifier<>(this); - private final Map<String, TimeSeriesCollection> dataset = new HashMap<>(); + private final Map<String, SampledDataset> dataset = new HashMap<>(); private final Map<String, JPanel> subPanels = new HashMap<>(); private final GridBagConstraints gcPanelConstraints; @@ -114,36 +118,46 @@ setLayout(new GridBagLayout()); } - private JPanel createCollectorDetailsPanel(TimeSeriesCollection timeSeriesCollection, String title) { + private JPanel createCollectorDetailsPanel(IntervalXYDataset timeSeriesCollection, String title, String units) { JPanel detailsPanel = new JPanel(); detailsPanel.setBorder(Components.smallBorder()); detailsPanel.setLayout(new BorderLayout()); detailsPanel.add(Components.header(title), BorderLayout.NORTH); - JFreeChart chart = ChartFactory.createTimeSeriesChart( - null, - localize(LocaleResources.VM_GC_COLLECTOR_CHART_REAL_TIME_LABEL), - localize(LocaleResources.VM_GC_COLLECTOR_CHART_GC_TIME_LABEL), - timeSeriesCollection, - false, false, false); + JFreeChart chart = ChartFactory.createHistogram( + null, + localize(LocaleResources.VM_GC_COLLECTOR_CHART_REAL_TIME_LABEL), + localize(LocaleResources.VM_GC_COLLECTOR_CHART_GC_TIME_LABEL, units), + timeSeriesCollection, + PlotOrientation.VERTICAL, + false, + false, + false); + ((XYBarRenderer)(chart.getXYPlot().getRenderer())).setBarPainter(new StandardXYBarPainter()); + chart.getXYPlot().setDomainAxis(new DateAxis()); JPanel chartPanel = new RecentTimeSeriesChartPanel(new RecentTimeSeriesChartController(chart)); + NumberAxis axis = (NumberAxis) chart.getXYPlot().getRangeAxis(); + + axis.setRangeType(RangeType.POSITIVE); + axis.setAutoRange(true); + axis.setAutoRangeMinimumSize(1); + detailsPanel.add(chartPanel, BorderLayout.CENTER); return detailsPanel; } @Override - public void addChart(final String tag, final String title) { + public void addChart(final String tag, final String title, final String units) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - TimeSeries timeSeries = new TimeSeries(tag); - TimeSeriesCollection timeSeriesCollection = new TimeSeriesCollection(timeSeries); - dataset.put(tag, timeSeriesCollection); - JPanel subPanel = createCollectorDetailsPanel(timeSeriesCollection, title); + SampledDataset newData = new SampledDataset(); + dataset.put(tag, newData); + JPanel subPanel = createCollectorDetailsPanel(newData, title, units); subPanels.put(tag, subPanel); add(subPanel, gcPanelConstraints); gcPanelConstraints.gridy++; @@ -167,14 +181,14 @@ } @Override - public void addData(final String tag, List<DiscreteTimeData<? extends Number>> data) { - final List<DiscreteTimeData<? extends Number>> copy = new ArrayList<>(data); + public void addData(final String tag, List<IntervalTimeData<Double>> data) { + final List<IntervalTimeData<Double>> copy = new ArrayList<>(data); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - TimeSeries series = dataset.get(tag).getSeries(tag); - for (DiscreteTimeData<? extends Number> timeData: copy) { - series.add(new FixedMillisecond(timeData.getTimeInMillis()), timeData.getData(), false); + SampledDataset series = dataset.get(tag); + for (IntervalTimeData<Double> timeData: copy) { + series.add(timeData.getStartTimeInMillis(), timeData.getEndTimeInMillis(), timeData.getData()); } series.fireSeriesChanged(); } @@ -186,7 +200,7 @@ SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - TimeSeries series = dataset.get(tag).getSeries(tag); + SampledDataset series = dataset.get(tag); series.clear(); } });
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcView.java Wed May 23 12:03:47 2012 -0400 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/VmGcView.java Wed May 23 12:41:57 2012 -0400 @@ -41,7 +41,7 @@ import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.View; -import com.redhat.thermostat.common.model.DiscreteTimeData; +import com.redhat.thermostat.common.model.IntervalTimeData; public interface VmGcView extends View { @@ -54,11 +54,11 @@ void removeActionListener(ActionListener<Action> listener); - void addChart(String tag, String title); + void addChart(String tag, String title, String valueUnit); void removeChart(String tag); - void addData(String tag, List<DiscreteTimeData<? extends Number>> data); + void addData(String tag, List<IntervalTimeData<Double>> data); void clearData(String tag);
--- a/client/core/src/main/resources/com/redhat/thermostat/client/locale/strings.properties Wed May 23 12:03:47 2012 -0400 +++ b/client/core/src/main/resources/com/redhat/thermostat/client/locale/strings.properties Wed May 23 12:41:57 2012 -0400 @@ -132,7 +132,7 @@ 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 (s) +VM_GC_COLLECTOR_CHART_GC_TIME_LABEL = GC Time ({0}) VM_LOADED_CLASSES = Loaded Classes VM_CLASSES_CHART_REAL_TIME_LABEL = Time
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/test/java/com/redhat/thermostat/client/ui/VmGcControllerTest.java Wed May 23 12:41:57 2012 -0400 @@ -0,0 +1,172 @@ +/* + * 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 static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.isNotNull; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.common.Timer.SchedulingType; +import com.redhat.thermostat.common.TimerFactory; +import com.redhat.thermostat.common.ViewFactory; +import com.redhat.thermostat.common.appctx.ApplicationContext; +import com.redhat.thermostat.common.appctx.ApplicationContextUtil; +import com.redhat.thermostat.common.dao.DAOFactory; +import com.redhat.thermostat.common.dao.VmGcStatDAO; +import com.redhat.thermostat.common.dao.VmMemoryStatDAO; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.model.VmGcStat; +import com.redhat.thermostat.common.model.VmMemoryStat; +import com.redhat.thermostat.common.model.VmMemoryStat.Generation; + +public class VmGcControllerTest { + + private Timer timer; + private Runnable timerAction; + private VmGcView view; + private ActionListener<VmGcView.Action> viewListener; + + @Before + public void setUp() { + ApplicationContextUtil.resetApplicationContext(); + + // Setup Timer + timer = mock(Timer.class); + ArgumentCaptor<Runnable> timerActionCaptor = ArgumentCaptor.forClass(Runnable.class); + doNothing().when(timer).setAction(timerActionCaptor.capture()); + + TimerFactory timerFactory = mock(TimerFactory.class); + when(timerFactory.createTimer()).thenReturn(timer); + ApplicationContext.getInstance().setTimerFactory(timerFactory); + + // Set up fake data + List<VmGcStat> stats = new ArrayList<>(); + VmGcStat stat1 = new VmGcStat(42, 1, "collector1", 1, 10); + VmGcStat stat2 = new VmGcStat(42, 2, "collector1", 5, 20); + stats.add(stat1); + stats.add(stat2); + + Generation gen; + List<Generation> gens = new ArrayList<>(); + gen = new Generation(); + gen.name = "generation 1"; + gen.collector = "collector1"; + gens.add(gen); + VmMemoryStat memoryStat = new VmMemoryStat(1, 42, gens); + + // Setup DAO + VmGcStatDAO vmGcStatDAO = mock(VmGcStatDAO.class); + when(vmGcStatDAO.getLatestVmGcStats(isA(VmRef.class))).thenReturn(stats); + VmMemoryStatDAO vmMemoryStatDAO = mock(VmMemoryStatDAO.class); + when(vmMemoryStatDAO.getLatestMemoryStat(isA(VmRef.class))).thenReturn(memoryStat); + + DAOFactory daoFactory = mock(DAOFactory.class); + when(daoFactory.getVmGcStatDAO()).thenReturn(vmGcStatDAO); + when(daoFactory.getVmMemoryStatDAO()).thenReturn(vmMemoryStatDAO); + ApplicationContext.getInstance().setDAOFactory(daoFactory); + + // Setup View + view = mock(VmGcView.class); + ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class); + doNothing().when(view).addActionListener(viewArgumentCaptor.capture()); + + ViewFactory viewFactory = mock(ViewFactory.class); + when(viewFactory.getView(eq(VmGcView.class))).thenReturn(view); + + ApplicationContext.getInstance().setViewFactory(viewFactory); + + // Now start the controller + VmRef ref = mock(VmRef.class); + + new VmGcController(ref); + + // Extract relevant objects + viewListener = viewArgumentCaptor.getValue(); + timerAction = timerActionCaptor.getValue(); + } + + @After + public void tearDown() { + ApplicationContextUtil.resetApplicationContext(); + } + + @Test + public void verifyTimer() { + verify(timer).setAction(isNotNull(Runnable.class)); + verify(timer).setAction(isA(Runnable.class)); + verify(timer).setDelay(5); + verify(timer).setInitialDelay(0); + verify(timer).setTimeUnit(TimeUnit.SECONDS); + verify(timer).setSchedulingType(SchedulingType.FIXED_RATE); + + } + + @Test + public void verifyStartAndStop() { + viewListener.actionPerformed(new ActionEvent<>(view, VmGcView.Action.VISIBLE)); + + verify(timer).start(); + + viewListener.actionPerformed(new ActionEvent<>(view, VmGcView.Action.HIDDEN)); + + verify(timer).stop(); + } + + @Test + public void verifyAction() { + timerAction.run(); + + verify(view).addData(isA(String.class), isA(List.class)); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/model/IntervalTimeData.java Wed May 23 12:41:57 2012 -0400 @@ -0,0 +1,63 @@ +/* + * 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.common.model; + +public class IntervalTimeData<T> { + + private long start; + private long end; + private T data; + + public IntervalTimeData(long start, long end, T data) { + this.start = start; + this.end = end; + this.data = data; + } + + public long getStartTimeInMillis() { + return start; + } + + public long getEndTimeInMillis() { + return end; + } + + public T getData() { + return data; + } + +}
--- a/distribution/config/osgi-export.properties Wed May 23 12:03:47 2012 -0400 +++ b/distribution/config/osgi-export.properties Wed May 23 12:41:57 2012 -0400 @@ -41,6 +41,7 @@ org.jfree.chart.labels org.jfree.chart.plot org.jfree.chart.renderer.xy +org.jfree.data org.jfree.data.time org.jfree.data.xy