Mercurial > hg > release > thermostat-0.9
changeset 103:dd82a9bcf94c
Implement client side for showing number of loaded classes.
line wrap: on
line diff
--- a/client/pom.xml Tue Mar 06 22:43:32 2012 +0100 +++ b/client/pom.xml Tue Mar 06 22:44:19 2012 +0100 @@ -59,6 +59,12 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>1.9.0</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>com.redhat.thermostat</groupId> <artifactId>thermostat-common</artifactId> <version>${project.version}</version>
--- a/client/src/main/java/com/redhat/thermostat/client/Connection.java Tue Mar 06 22:43:32 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -/* - * 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; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public abstract class Connection { - - public enum ConnectionType { - LOCAL(false), - REMOTE(true), - CLUSTER(true), - ; - - boolean isDisplayable = false; - boolean needsUrl = false; - - private ConnectionType(boolean needsUrl) { - this.needsUrl = needsUrl; - } - - private ConnectionType(boolean isDisplayable, boolean needsUrl) { - this.isDisplayable = isDisplayable; - } - - public boolean isDisplayable() { - return isDisplayable; - } - - public boolean needsUrl() { - return needsUrl; - } - } - - public enum ConnectionStatus { - CONNECTED, - FAILED_TO_CONNECT, - DISCONNECTED, - } - - public interface ConnectionListener { - public void changed(ConnectionStatus newStatus); - } - - protected boolean connected = false; - - private ConnectionType type; - private String url; - - private List<ConnectionListener> listeners = new CopyOnWriteArrayList<ConnectionListener>(); - - public void setType(ConnectionType type) { - this.type = type; - } - - public ConnectionType getType() { - return type; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getUrl() { - return url; - } - - @Override - public String toString() { - if (url == null) { - return type.toString(); - } - return type.toString() + " to " + url; - } - - public abstract void connect(); - - public abstract void disconnect(); - - public boolean isConnected() { - return connected; - } - - public void addListener(ConnectionListener listener) { - this.listeners.add(listener); - } - - public void removeListener(ConnectionListener listener) { - this.listeners.remove(listener); - } - - protected void fireChanged(ConnectionStatus status) { - for (ConnectionListener listener: listeners) { - listener.changed(status); - } - } -}
--- a/client/src/main/java/com/redhat/thermostat/client/Main.java Tue Mar 06 22:43:32 2012 +0100 +++ b/client/src/main/java/com/redhat/thermostat/client/Main.java Tue Mar 06 22:44:19 2012 +0100 @@ -46,12 +46,19 @@ import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; -import com.redhat.thermostat.client.Connection.ConnectionListener; -import com.redhat.thermostat.client.Connection.ConnectionStatus; +import com.redhat.thermostat.client.appctx.ApplicationContext; import com.redhat.thermostat.client.locale.LocaleResources; import com.redhat.thermostat.client.ui.ConnectionSelectionDialog; import com.redhat.thermostat.client.ui.MainWindow; import com.redhat.thermostat.common.Constants; +import com.redhat.thermostat.common.dao.Connection; +import com.redhat.thermostat.common.dao.Connection.ConnectionListener; +import com.redhat.thermostat.common.dao.Connection.ConnectionStatus; +import com.redhat.thermostat.common.dao.ConnectionProvider; +import com.redhat.thermostat.common.dao.DAOFactory; +import com.redhat.thermostat.common.dao.MongoConnection; +import com.redhat.thermostat.common.dao.MongoConnectionProvider; +import com.redhat.thermostat.common.dao.MongoDAOFactory; import com.redhat.thermostat.common.utils.LoggingUtils; public class Main { @@ -59,7 +66,6 @@ private static final Logger logger = LoggingUtils.getLogger(Main.class); private ClientArgs arguments; - private Connection connection; private UiFacadeFactory uiFacadeFactory; private Main(String[] args) { @@ -70,12 +76,15 @@ System.exit(-1); } - connection = new MongoConnection(arguments.getProperties()); + ConnectionProvider connProv = new MongoConnectionProvider(arguments.getProperties()); + DAOFactory daoFactory = new MongoDAOFactory(connProv); + ApplicationContext.getInstance().setDAOFactory(daoFactory); } private void showGui() { JPopupMenu.setDefaultLightWeightPopupEnabled(false); + Connection connection = ApplicationContext.getInstance().getDAOFactory().getConnection(); ConnectionSelectionDialog dialog = new ConnectionSelectionDialog((JFrame) null, connection); dialog.pack(); dialog.setModal(true);
--- a/client/src/main/java/com/redhat/thermostat/client/MongoConnection.java Tue Mar 06 22:43:32 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -/* - * 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; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.UnknownHostException; -import java.util.Properties; -import java.util.logging.Logger; - -import com.mongodb.DB; -import com.mongodb.Mongo; -import com.mongodb.MongoException; -import com.mongodb.MongoURI; -import com.redhat.thermostat.common.Constants; -import com.redhat.thermostat.common.NotImplementedException; -import com.redhat.thermostat.common.storage.StorageConstants; -import com.redhat.thermostat.common.utils.LoggedExternalProcess; -import com.redhat.thermostat.common.utils.LoggingUtils; - -public class MongoConnection extends Connection { - private static final Logger logger = LoggingUtils.getLogger(MongoConnection.class); - - private Mongo m = null; - private DB db = null; - private boolean hasLocalAgent = false; - private Process localAgentProcess = null; - private Properties props; - - public MongoConnection(Properties props) { - this.props = props; - } - - @Override - public void connect() { - try { - m = new Mongo(getMongoURI()); - db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME); - /* the mongo java driver does not ensure this connection is actually working */ - testConnection(db); - } catch (MongoException e) { - fireChanged(ConnectionStatus.FAILED_TO_CONNECT); - return; - } catch (UnknownHostException e) { - fireChanged(ConnectionStatus.FAILED_TO_CONNECT); - return; - } catch (LocalAgentException e) { - fireChanged(ConnectionStatus.FAILED_TO_CONNECT); - return; - } catch (NotImplementedException e) { - fireChanged(ConnectionStatus.FAILED_TO_CONNECT); - return; - } - fireChanged(ConnectionStatus.CONNECTED); - connected = true; - } - - private static void testConnection(DB db) { - db.getCollection("agent-config").getCount(); - } - - private MongoURI getMongoURI() { - MongoURI uri = null; - switch (getType()) { - case LOCAL: - startLocalAgent(); - uri = new MongoURI("mongodb://127.0.0.1:" - + props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT)); - break; - case REMOTE: - throw new NotImplementedException("No mongo URI implemented for REMOTE."); - case CLUSTER: - throw new NotImplementedException("No mongo URI implemented for CLUSTER."); - } - return uri; - } - - private void startLocalAgent() throws LocalAgentException { - int status = 0; - try { - String agentScript = props.getProperty(Constants.CLIENT_PROPERTY_AGENT_LAUNCH_SCRIPT); - localAgentProcess = new LoggedExternalProcess(new String[] { agentScript, "--local" }).runAndReturnProcess(); - // Allow some time for things to get started. - try { - // TODO provide some UI feedback here instead of just seeming dead. - Thread.sleep(2000); - } catch (InterruptedException e) { - // ignore - } - } catch (IOException e) { - throw new LocalAgentException(); - } - if (status != 0) { - throw new LocalAgentException(); - } - hasLocalAgent = true; - } - - public DB getDB() { - return db; - } - - @Override - public void disconnect() { - if (m != null) { - m.close(); - } - if (hasLocalAgent) { - stopLocalAgent(); - } - connected = false; - } - - private void stopLocalAgent() { - // TODO this is currently using Agent's 'run until some data avail on stdin' hack. - // That hack will go away, at which point we will need another way to shut down. - OutputStream agentIn = localAgentProcess.getOutputStream(); - byte[] anything = { 0x04 }; - try { - agentIn.write(anything); - agentIn.flush(); - localAgentProcess.waitFor(); - } catch (IOException e) { - logger.warning("Error shutting down local agent."); - } catch (InterruptedException e) { - logger.warning("Interrupted waiting for local agent to shut down."); - } - } - - private class LocalAgentException extends RuntimeException { - } -}
--- a/client/src/main/java/com/redhat/thermostat/client/UiFacadeFactoryImpl.java Tue Mar 06 22:43:32 2012 +0100 +++ b/client/src/main/java/com/redhat/thermostat/client/UiFacadeFactoryImpl.java Tue Mar 06 22:44:19 2012 +0100 @@ -37,10 +37,12 @@ package com.redhat.thermostat.client; import com.redhat.thermostat.common.dao.HostRef; +import com.redhat.thermostat.common.dao.MongoConnection; import com.redhat.thermostat.common.dao.VmRef; public class UiFacadeFactoryImpl implements UiFacadeFactory { + // TODO: Eventually, this should disappear and be completely provided by the DAOFactory. private MongoConnection connection; public UiFacadeFactoryImpl(MongoConnection connection) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/VmClassStatController.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,164 @@ +/* + * 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; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +import javax.swing.SwingWorker; + +import org.jfree.data.time.FixedMillisecond; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; + +import com.redhat.thermostat.client.appctx.ApplicationContext; +import com.redhat.thermostat.client.ui.VmClassStatPanel; +import com.redhat.thermostat.client.ui.VmClassStatView; +import com.redhat.thermostat.common.VmClassStat; +import com.redhat.thermostat.common.dao.VmClassStatDAO; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.utils.LoggingUtils; + +public class VmClassStatController implements AsyncUiFacade { + + private static final Logger logger = LoggingUtils.getLogger(VmClassStatController.class); + + private class UpdateChartDataTimerTask extends TimerTask { + + @Override + public void run() { + UpdateChartDataWorker worker = new UpdateChartDataWorker(); + worker.execute(); + } + + } + + private class UpdateChartDataWorker extends SwingWorker<List<DiscreteTimeData<Long>>, Void> { + + @Override + protected List<DiscreteTimeData<Long>> doInBackground() throws Exception { + List<VmClassStat> latestClassStats = dao.getLatestClassStats(); + List<DiscreteTimeData<Long>> timeData = new ArrayList<>(); + for (VmClassStat stat : latestClassStats) { + timeData.add(new DiscreteTimeData<Long>(stat.getTimestamp(), stat.getLoadedClasses())); + } + + return timeData; + } + + @Override + protected void done() { + try { + appendCollectorDataToChart(get(), classSeries); + } catch (ExecutionException ee) { + logger.throwing("VmClassStatController.UpdateChartDataWorker", "done", ee); + } catch (InterruptedException ie) { + // Preserve interrupted flag to let the EDT handle this. + Thread.currentThread().interrupt(); + } + } + + private void appendCollectorDataToChart(List<DiscreteTimeData<Long>> collectorData, TimeSeries collectorSeries) { + if (collectorData.size() > 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. + */ + + for (DiscreteTimeData<Long> data : collectorData) { + collectorSeries.add( + new FixedMillisecond(data.getTimeInMillis()), data.getData(), + /* notify = */false); + } + + collectorSeries.fireSeriesChanged(); + } + + } + } + + private VmClassStatView classesView; + + // TODO: Use application wide ScheduledExecutorService thread pool. + private Timer timer; + + private VmClassStatDAO dao; + + private TimeSeries classSeries; + private TimeSeriesCollection classSeriesCollection; + + public VmClassStatController(VmRef ref) { + dao = ApplicationContext.getInstance().getDAOFactory().getVmClassStatsDAO(ref); + classesView = createView(); + classSeries = new TimeSeries("loadedClasses"); + classSeriesCollection = new TimeSeriesCollection(classSeries); + classesView.setDataSet(classSeriesCollection); + } + + protected VmClassStatView createView() { + return new VmClassStatPanel(); + } + + @Override + public void start() { + if (timer == null) { + timer = new Timer(); + } + TimerTask updateTimerTask = new UpdateChartDataTimerTask(); + timer.scheduleAtFixedRate(updateTimerTask, 0, TimeUnit.SECONDS.toMillis(5)); + } + + @Override + public void stop() { + timer.cancel(); + timer = null; + } + + public Component getComponent() { + return classesView.getUIComponent(); + } + +}
--- a/client/src/main/java/com/redhat/thermostat/client/VmPanelFacade.java Tue Mar 06 22:43:32 2012 +0100 +++ b/client/src/main/java/com/redhat/thermostat/client/VmPanelFacade.java Tue Mar 06 22:44:19 2012 +0100 @@ -82,4 +82,6 @@ public ChangeableText getVmArguments(); + public VmClassStatController getClassesController(); + }
--- a/client/src/main/java/com/redhat/thermostat/client/VmPanelFacadeImpl.java Tue Mar 06 22:43:32 2012 +0100 +++ b/client/src/main/java/com/redhat/thermostat/client/VmPanelFacadeImpl.java Tue Mar 06 22:44:19 2012 +0100 @@ -99,6 +99,8 @@ private VmMemoryStat cached; + private VmClassStatController classesController; + public VmPanelFacadeImpl(VmRef vmRef, DB db) { this.ref = vmRef; vmInfoCollection = db.getCollection("vm-info"); @@ -106,6 +108,8 @@ vmMemoryStatsCollection = db.getCollection("vm-memory-stats"); vmRunningTimeFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.FULL); + + classesController = new VmClassStatController(vmRef); } @Override @@ -167,11 +171,15 @@ }, 0, TimeUnit.SECONDS.toMillis(5)); + classesController.start(); + } @Override public void stop() { timer.cancel(); + + classesController.stop(); } @Override @@ -458,4 +466,9 @@ return currentMemoryDataset; } + @Override + public VmClassStatController getClassesController() { + return classesController; + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/appctx/ApplicationContext.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,68 @@ +/* + * 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.appctx; + +import com.redhat.thermostat.common.dao.DAOFactory; + +public class ApplicationContext { + + private static ApplicationContext instance = new ApplicationContext(); + + private DAOFactory daoFactory; + + public static ApplicationContext getInstance() { + return instance; + } + + static void reset() { + instance = new ApplicationContext(); + } + + private ApplicationContext() { + // Nothing to do here, just prevent instantiation of this class outside + // the factory method. + } + + public void setDAOFactory(DAOFactory daoFactory) { + this.daoFactory = daoFactory; + } + + public DAOFactory getDAOFactory() { + return daoFactory; + } + +}
--- a/client/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java Tue Mar 06 22:43:32 2012 +0100 +++ b/client/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java Tue Mar 06 22:44:19 2012 +0100 @@ -162,7 +162,12 @@ VM_GC_COLLECTOR_OVER_GENERATION, VM_GC_COLLECTOR_CHART_REAL_TIME_LABEL, - VM_GC_COLLECTOR_CHART_GC_TIME_LABEL; + VM_GC_COLLECTOR_CHART_GC_TIME_LABEL, + + VM_INFO_TAB_CLASSES, + VM_LOADED_CLASSES, + VM_CLASSES_CHART_REAL_TIME_LABEL, + VM_CLASSES_CHART_LOADED_CLASSES_LABEL; static final String RESOURCE_BUNDLE = "com.redhat.thermostat.client.locale.strings";
--- a/client/src/main/java/com/redhat/thermostat/client/ui/ConnectionSelectionDialog.java Tue Mar 06 22:43:32 2012 +0100 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/ConnectionSelectionDialog.java Tue Mar 06 22:44:19 2012 +0100 @@ -57,9 +57,9 @@ import javax.swing.JLabel; import javax.swing.JPanel; -import com.redhat.thermostat.client.Connection; -import com.redhat.thermostat.client.Connection.ConnectionType; import com.redhat.thermostat.client.locale.LocaleResources; +import com.redhat.thermostat.common.dao.Connection; +import com.redhat.thermostat.common.dao.Connection.ConnectionType; public class ConnectionSelectionDialog extends JDialog {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmClassStatPanel.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,90 @@ +/* + * 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 com.redhat.thermostat.client.locale.Translate.localize; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; + +import javax.swing.JPanel; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.data.time.TimeSeriesCollection; + +import com.redhat.thermostat.client.locale.LocaleResources; + +public class VmClassStatPanel extends JPanel implements VmClassStatView { + + private Component chartPanel; + + public VmClassStatPanel() { + setBorder(Components.smallBorder()); + setLayout(new BorderLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.fill = GridBagConstraints.BOTH; + + add(Components.header(localize(LocaleResources.VM_LOADED_CLASSES)), BorderLayout.NORTH); + } + + public void setDataSet(TimeSeriesCollection dataset) { + if (chartPanel != null) { + remove(chartPanel); + } + + JFreeChart chart = ChartFactory.createTimeSeriesChart( + null, + localize(LocaleResources.VM_CLASSES_CHART_REAL_TIME_LABEL), + localize(LocaleResources.VM_CLASSES_CHART_LOADED_CLASSES_LABEL), + dataset, + false, false, false); + + chartPanel = new RecentTimeSeriesChartPanel(new RecentTimeSeriesChartController(chart)); + + add(chartPanel, BorderLayout.CENTER); + + } + + @Override + public Component getUIComponent() { + return this; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmClassStatView.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,47 @@ +/* + * 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.Component; + +import org.jfree.data.time.TimeSeriesCollection; + +public interface VmClassStatView { + + void setDataSet(TimeSeriesCollection dataset); + Component getUIComponent(); +}
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmPanel.java Tue Mar 06 22:43:32 2012 +0100 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmPanel.java Tue Mar 06 22:44:19 2012 +0100 @@ -82,6 +82,7 @@ tabPane.insertTab(localize(LocaleResources.VM_INFO_TAB_MEMORY), null, createMemoryPanel(), null, 1); tabPane.insertTab(localize(LocaleResources.VM_INFO_TAB_GC), null, createGcPanel(), localize(LocaleResources.GARBAGE_COLLECTION), 2); + tabPane.insertTab(localize(LocaleResources.VM_INFO_TAB_CLASSES), null, facade.getClassesController().getComponent(), null, 3); // TODO additional tabs provided by plugins // tabPane.insertTab(title, icon, component, tip, 3)
--- a/client/src/main/resources/com/redhat/thermostat/client/locale/strings.properties Tue Mar 06 22:43:32 2012 +0100 +++ b/client/src/main/resources/com/redhat/thermostat/client/locale/strings.properties Tue Mar 06 22:44:19 2012 +0100 @@ -126,3 +126,8 @@ 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_LOADED_CLASSES = Loaded Classes +VM_CLASSES_CHART_REAL_TIME_LABEL = Time +VM_CLASSES_CHART_LOADED_CLASSES_LABEL = Number of loaded classes +VM_INFO_TAB_CLASSES = Classes
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/test/java/com/redhat/thermostat/client/VmClassStatControllerTest.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,116 @@ +/* + * 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; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doAnswer; +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 org.jfree.data.general.DatasetChangeEvent; +import org.jfree.data.general.DatasetChangeListener; +import org.jfree.data.time.TimeSeriesCollection; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import com.redhat.thermostat.client.appctx.ApplicationContext; +import com.redhat.thermostat.client.ui.VmClassStatView; +import com.redhat.thermostat.common.VmClassStat; +import com.redhat.thermostat.common.dao.DAOFactory; +import com.redhat.thermostat.common.dao.MongoDAOFactory; +import com.redhat.thermostat.common.dao.MongoVmClassStatDAO; +import com.redhat.thermostat.common.dao.VmRef; + +public class VmClassStatControllerTest { + + @Test + public void testChartUpdate() { + + VmClassStat stat1 = new VmClassStat(123, 12345, 1234); + List<VmClassStat> stats = new ArrayList<VmClassStat>(); + stats.add(stat1); + + MongoVmClassStatDAO vmClassStatDAO = mock(MongoVmClassStatDAO.class); + when(vmClassStatDAO.getLatestClassStats()).thenReturn(stats).thenReturn(new ArrayList<VmClassStat>()); + + DAOFactory daoFactory = mock(MongoDAOFactory.class); + when(daoFactory.getVmClassStatsDAO(any(VmRef.class))).thenReturn(vmClassStatDAO); + + ApplicationContext.getInstance().setDAOFactory(daoFactory); + VmRef ref = mock(VmRef.class); + + final DatasetChangeListener l = mock(DatasetChangeListener.class); + final VmClassStatView view = mock(VmClassStatView.class); + doAnswer(new Answer<Void>() { + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + TimeSeriesCollection timeSeriesColl = (TimeSeriesCollection) args[0]; + timeSeriesColl.addChangeListener(l); + return null; + } + + }).when(view).setDataSet(any(TimeSeriesCollection.class)); + + // TODO: Consider to pass the ClassesView or a factory for it to the controller instead. + VmClassStatController controller = new VmClassStatController(ref) { + protected VmClassStatView createView() { + return view; + } + }; + + controller.start(); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // Get out of here ASAP. + return; + } + + verify(l, atLeast(1)).datasetChanged(any(DatasetChangeEvent.class)); + // We don't verify atMost() since we might increase the update rate in the future. + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/test/java/com/redhat/thermostat/client/appctx/ApplicationContextTest.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,95 @@ +/* + * 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.appctx; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.client.appctx.ApplicationContext; +import com.redhat.thermostat.common.dao.DAOFactory; + +public class ApplicationContextTest { + + @Before + public void setUp() { + ApplicationContext.reset(); + } + + @After + public void tearDown() { + ApplicationContext.reset(); + } + + @Test + public void verifyGetInstanceNotNull() { + ApplicationContext ctx = ApplicationContext.getInstance(); + assertNotNull(ctx); + } + + @Test + public void testDAOFactorySetGet() { + DAOFactory daoFactory = mock(DAOFactory.class); + ApplicationContext ctx = ApplicationContext.getInstance(); + ctx.setDAOFactory(daoFactory); + + DAOFactory actual1 = ctx.getDAOFactory(); + assertSame(daoFactory, actual1); + } + + @Test + public void verifyDAOFactoryIsNullWhenNotInitialized() { + ApplicationContext ctx = ApplicationContext.getInstance(); + + DAOFactory actual = ctx.getDAOFactory(); + assertNull(actual); + } + + @Test + public void verifyDAOFactoryStaysSame() { + DAOFactory daoFactory = mock(DAOFactory.class); + ApplicationContext ctx = ApplicationContext.getInstance(); + ctx.setDAOFactory(daoFactory); + + ApplicationContext ctx2 = ApplicationContext.getInstance(); + DAOFactory actual2 = ctx2.getDAOFactory(); + assertSame(daoFactory, actual2); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/test/java/com/redhat/thermostat/client/appctx/ApplicationContextUtil.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,51 @@ +/* + * 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.appctx; + +public class ApplicationContextUtil { + + /** + * This is here to allow tests to reset the ApplicationContext, while + * preventing real code the same (ApplicationContext.reset() is package private). + * + * It is vital that tests call this from their setUp() and tearDown() methods, + * to avoid leaking mocks and stuff from test to test. + */ + public static void resetApplicationContext() { + ApplicationContext.reset(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/test/java/com/redhat/thermostat/client/ui/VmClassStatPanelTest.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,56 @@ +/* + * 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.junit.Assert.assertEquals; + +import org.jfree.data.time.TimeSeriesCollection; +import org.junit.Test; + +public class VmClassStatPanelTest { + + @Test + public void testSetDataSetTwice() { + VmClassStatPanel panel = new VmClassStatPanel(); + TimeSeriesCollection dataSet = new TimeSeriesCollection(); + panel.setDataSet(dataSet); + int numComponents = panel.getComponentCount(); + panel.setDataSet(dataSet); + assertEquals(numComponents, panel.getComponentCount()); + } + +}
--- a/common/pom.xml Tue Mar 06 22:43:32 2012 +0100 +++ b/common/pom.xml Tue Mar 06 22:44:19 2012 +0100 @@ -75,6 +75,18 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <version>1.4.11</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>1.4.11</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.7.3</version>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/Connection.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,132 @@ +/* + * 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.dao; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public abstract class Connection { + + public enum ConnectionType { + LOCAL(false), + REMOTE(true), + CLUSTER(true), + ; + + boolean isDisplayable = false; + boolean needsUrl = false; + + private ConnectionType(boolean needsUrl) { + this.needsUrl = needsUrl; + } + + private ConnectionType(boolean isDisplayable, boolean needsUrl) { + this.isDisplayable = isDisplayable; + } + + public boolean isDisplayable() { + return isDisplayable; + } + + public boolean needsUrl() { + return needsUrl; + } + } + + public enum ConnectionStatus { + CONNECTED, + FAILED_TO_CONNECT, + DISCONNECTED, + } + + public interface ConnectionListener { + public void changed(ConnectionStatus newStatus); + } + + protected boolean connected = false; + + private ConnectionType type; + private String url; + + private List<ConnectionListener> listeners = new CopyOnWriteArrayList<ConnectionListener>(); + + public void setType(ConnectionType type) { + this.type = type; + } + + public ConnectionType getType() { + return type; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + @Override + public String toString() { + if (url == null) { + return type.toString(); + } + return type.toString() + " to " + url; + } + + public abstract void connect(); + + public abstract void disconnect(); + + public boolean isConnected() { + return connected; + } + + public void addListener(ConnectionListener listener) { + this.listeners.add(listener); + } + + public void removeListener(ConnectionListener listener) { + this.listeners.remove(listener); + } + + protected void fireChanged(ConnectionStatus status) { + for (ConnectionListener listener: listeners) { + listener.changed(status); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/ConnectionProvider.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,42 @@ +/* + * 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.dao; + +public interface ConnectionProvider { + + Connection createConnection(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,45 @@ +/* + * 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.dao; + +public interface DAOFactory { + + public abstract Connection getConnection(); + + public abstract VmClassStatDAO getVmClassStatsDAO(VmRef ref); + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,166 @@ +/* + * 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.dao; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.UnknownHostException; +import java.util.Properties; +import java.util.logging.Logger; + +import com.mongodb.DB; +import com.mongodb.Mongo; +import com.mongodb.MongoException; +import com.mongodb.MongoURI; +import com.redhat.thermostat.common.Constants; +import com.redhat.thermostat.common.NotImplementedException; +import com.redhat.thermostat.common.storage.StorageConstants; +import com.redhat.thermostat.common.utils.LoggedExternalProcess; +import com.redhat.thermostat.common.utils.LoggingUtils; + +public class MongoConnection extends Connection { + private static final Logger logger = LoggingUtils.getLogger(MongoConnection.class); + + private Mongo m = null; + private DB db = null; + private boolean hasLocalAgent = false; + private Process localAgentProcess = null; + private Properties props; + + public MongoConnection(Properties props) { + this.props = props; + } + + @Override + public void connect() { + try { + m = new Mongo(getMongoURI()); + db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME); + /* the mongo java driver does not ensure this connection is actually working */ + testConnection(db); + } catch (MongoException e) { + fireChanged(ConnectionStatus.FAILED_TO_CONNECT); + return; + } catch (UnknownHostException e) { + fireChanged(ConnectionStatus.FAILED_TO_CONNECT); + return; + } catch (LocalAgentException e) { + fireChanged(ConnectionStatus.FAILED_TO_CONNECT); + return; + } catch (NotImplementedException e) { + fireChanged(ConnectionStatus.FAILED_TO_CONNECT); + return; + } + fireChanged(ConnectionStatus.CONNECTED); + connected = true; + } + + private static void testConnection(DB db) { + db.getCollection("agent-config").getCount(); + } + + private MongoURI getMongoURI() { + MongoURI uri = null; + switch (getType()) { + case LOCAL: + startLocalAgent(); + uri = new MongoURI("mongodb://127.0.0.1:" + + props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT)); + break; + case REMOTE: + throw new NotImplementedException("No mongo URI implemented for REMOTE."); + case CLUSTER: + throw new NotImplementedException("No mongo URI implemented for CLUSTER."); + } + return uri; + } + + private void startLocalAgent() throws LocalAgentException { + int status = 0; + try { + String agentScript = props.getProperty(Constants.CLIENT_PROPERTY_AGENT_LAUNCH_SCRIPT); + localAgentProcess = new LoggedExternalProcess(new String[] { agentScript, "--local" }).runAndReturnProcess(); + // Allow some time for things to get started. + try { + // TODO provide some UI feedback here instead of just seeming dead. + Thread.sleep(2000); + } catch (InterruptedException e) { + // ignore + } + } catch (IOException e) { + throw new LocalAgentException(); + } + if (status != 0) { + throw new LocalAgentException(); + } + hasLocalAgent = true; + } + + public DB getDB() { + return db; + } + + @Override + public void disconnect() { + if (m != null) { + m.close(); + } + if (hasLocalAgent) { + stopLocalAgent(); + } + connected = false; + } + + private void stopLocalAgent() { + // TODO this is currently using Agent's 'run until some data avail on stdin' hack. + // That hack will go away, at which point we will need another way to shut down. + OutputStream agentIn = localAgentProcess.getOutputStream(); + byte[] anything = { 0x04 }; + try { + agentIn.write(anything); + agentIn.flush(); + localAgentProcess.waitFor(); + } catch (IOException e) { + logger.warning("Error shutting down local agent."); + } catch (InterruptedException e) { + logger.warning("Interrupted waiting for local agent to shut down."); + } + } + + private class LocalAgentException extends RuntimeException { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnectionProvider.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,54 @@ +/* + * 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.dao; + +import java.util.Properties; + +public class MongoConnectionProvider implements ConnectionProvider { + + private Properties connectionProperties; + + public MongoConnectionProvider(Properties connProps) { + connectionProperties = connProps; + } + + @Override + public Connection createConnection() { + return new MongoConnection(connectionProperties); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoDAOFactory.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,60 @@ +/* + * 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.dao; + +import java.util.Properties; + + +public class MongoDAOFactory implements DAOFactory { + + private MongoConnection connection; + + public MongoDAOFactory(ConnectionProvider connProv) { + connection = (MongoConnection) connProv.createConnection(); + } + + @Override + public Connection getConnection() { + return connection; + } + + @Override + public VmClassStatDAO getVmClassStatsDAO(VmRef ref) { + return new MongoVmClassStatDAO(connection.getDB(), ref); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoVmClassStatDAO.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,86 @@ +/* + * 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.dao; + +import java.util.ArrayList; +import java.util.List; + +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; +import com.redhat.thermostat.common.VmClassStat; + +public class MongoVmClassStatDAO implements VmClassStatDAO { + + private VmRef ref; + private DBCollection vmClassStatsCollection; + + private long lastUpdate = Long.MIN_VALUE; + + public MongoVmClassStatDAO(DB db, VmRef vmRef) { + ref = vmRef; + vmClassStatsCollection = db.getCollection("vm-class-stats"); + } + + @Override + public List<VmClassStat> getLatestClassStats() { + ArrayList<VmClassStat> result = new ArrayList<>(); + BasicDBObject queryObject = new BasicDBObject(); + queryObject.put("agent-id", ref.getAgent().getAgentId()); + queryObject.put("vm-id", Integer.valueOf(ref.getId())); + if (lastUpdate != 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 > " + lastUpdate); + } + DBCursor cursor = vmClassStatsCollection.find(queryObject); + long timestamp; + Long loadedClasses; + while (cursor.hasNext()) { + DBObject current = cursor.next(); + timestamp = (Long) current.get("timestamp"); + loadedClasses = (Long) current.get("loadedClasses"); + int vmId = (Integer) current.get("vm-id"); + result.add(new VmClassStat(vmId, timestamp, loadedClasses)); + lastUpdate = Math.max(timestamp, lastUpdate); + } + + return result; + } +}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAO.java Tue Mar 06 22:43:32 2012 +0100 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAO.java Tue Mar 06 22:44:19 2012 +0100 @@ -36,14 +36,21 @@ package com.redhat.thermostat.common.dao; +import java.util.List; + +import com.redhat.thermostat.common.VmClassStat; import com.redhat.thermostat.common.storage.Category; import com.redhat.thermostat.common.storage.Key; -public class VmClassStatDAO { +public interface VmClassStatDAO { static final Key<Integer> vmIdKey = new Key<>("vm-id", false); static final Key<Long> loadedClassesKey = new Key<>("loadedClasses", false); - public static final Category vmClassStatsCategory = new Category("vm-class-stats", - vmIdKey, Key.TIMESTAMP, loadedClassesKey); -} + + public static final Category vmClassStatsCategory = new Category( + "vm-class-stats", vmIdKey, Key.TIMESTAMP, loadedClassesKey); + + public abstract List<VmClassStat> getLatestClassStats(); + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,80 @@ +/* + * 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.dao; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +import com.mongodb.DB; + +public class MongoDAOFactoryTest { + + @Test + public void testGetConnection() { + Connection conn = mock(MongoConnection.class); + + ConnectionProvider connProvider = mock(ConnectionProvider.class); + when(connProvider.createConnection()).thenReturn(conn); + + DAOFactory daoFactory = new MongoDAOFactory(connProvider); + assertSame(conn, daoFactory.getConnection()); + + } + + @Test + public void testGetVmClassStatsDAO() { + + DB db = mock(DB.class); + + MongoConnection conn = mock(MongoConnection.class); + when(conn.getDB()).thenReturn(db); + + ConnectionProvider connProv = mock(ConnectionProvider.class); + when(connProv.createConnection()).thenReturn(conn); + + DAOFactory instance = new MongoDAOFactory(connProv); + + VmRef ref = mock(VmRef.class); + VmClassStatDAO vmClassStatsDAO = instance.getVmClassStatsDAO(ref); + + assertNotNull(vmClassStatsDAO); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/test/java/com/redhat/thermostat/common/dao/MongoVmClassStatDAOTest.java Tue Mar 06 22:44:19 2012 +0100 @@ -0,0 +1,154 @@ +/* + * 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.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collection; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; +import com.redhat.thermostat.common.VmClassStat; +import com.redhat.thermostat.common.storage.Key; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({DBCollection.class, DB.class }) +public class MongoVmClassStatDAOTest { + + @Test + public void testCategory() { + assertEquals("vm-class-stats", VmClassStatDAO.vmClassStatsCategory.getName()); + Collection<Key<?>> keys = VmClassStatDAO.vmClassStatsCategory.getKeys(); + assertTrue(keys.contains(new Key<Integer>("vm-id", false))); + assertTrue(keys.contains(new Key<Long>("timestamp", false))); + assertTrue(keys.contains(new Key<Long>("loadedClasses", false))); + assertEquals(3, keys.size()); + + } + + @Test + public void testGetLatestClassStatsBasic() { + + DBObject dbValue = PowerMockito.mock(DBObject.class); + when(dbValue.get("timestamp")).thenReturn(1234L); + when(dbValue.get("vm-id")).thenReturn(321); + when(dbValue.get("loadedClasses")).thenReturn(12345L); + + DBCursor cursor = mock(DBCursor.class); + when(cursor.hasNext()).thenReturn(true).thenReturn(false); + when(cursor.next()).thenReturn(dbValue); + + DBCollection vmClassStatsCollection = PowerMockito.mock(DBCollection.class); + when(vmClassStatsCollection.find(any(DBObject.class))).thenReturn(cursor); + + DB db = mock(DB.class); + when(db.getCollection("vm-class-stats")).thenReturn(vmClassStatsCollection); + + HostRef hostRef = mock(HostRef.class); + when(hostRef.getAgentId()).thenReturn("system"); + + VmRef vmRef = mock(VmRef.class); + when(vmRef.getAgent()).thenReturn(hostRef); + when(vmRef.getId()).thenReturn("321"); + + + VmClassStatDAO dao = new MongoVmClassStatDAO(db, vmRef); + List<VmClassStat> vmClassStats = dao.getLatestClassStats(); + + ArgumentCaptor<DBObject> arg = ArgumentCaptor.forClass(DBObject.class); + verify(vmClassStatsCollection).find(arg.capture()); + assertNull(arg.getValue().get("$where")); + + assertEquals(1, vmClassStats.size()); + VmClassStat stat = vmClassStats.get(0); + assertEquals(1234L, stat.getTimestamp()); + assertEquals(12345L, stat.getLoadedClasses()); + assertEquals(321, stat.getVmId()); + } + + @Test + public void testGetLatestClassStatsTwice() { + + DBObject dbValue = PowerMockito.mock(DBObject.class); + when(dbValue.get("timestamp")).thenReturn(1234L); + when(dbValue.get("vm-id")).thenReturn(321); + when(dbValue.get("loadedClasses")).thenReturn(12345L); + + DBCursor cursor = mock(DBCursor.class); + when(cursor.hasNext()).thenReturn(true).thenReturn(false); + when(cursor.next()).thenReturn(dbValue); + + DBCollection vmClassStatsCollection = PowerMockito.mock(DBCollection.class); + when(vmClassStatsCollection.find(any(DBObject.class))).thenReturn(cursor); + + DB db = mock(DB.class); + when(db.getCollection("vm-class-stats")).thenReturn(vmClassStatsCollection); + + HostRef hostRef = mock(HostRef.class); + when(hostRef.getAgentId()).thenReturn("system"); + + VmRef vmRef = mock(VmRef.class); + when(vmRef.getAgent()).thenReturn(hostRef); + when(vmRef.getId()).thenReturn("321"); + + + VmClassStatDAO dao = new MongoVmClassStatDAO(db, vmRef); + dao.getLatestClassStats(); + + dao.getLatestClassStats(); + ArgumentCaptor<DBObject> arg = ArgumentCaptor.forClass(DBObject.class); + verify(vmClassStatsCollection, times(2)).find(arg.capture()); + assertEquals("this.timestamp > 1234", arg.getValue().get("$where")); + } +}
--- a/common/src/test/java/com/redhat/thermostat/common/dao/VmClassStatDAOTest.java Tue Mar 06 22:43:32 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * 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.dao; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Collection; - -import org.junit.Test; - -import com.redhat.thermostat.common.storage.Key; - -public class VmClassStatDAOTest { - - @Test - public void testCategory() { - assertEquals("vm-class-stats", VmClassStatDAO.vmClassStatsCategory.getName()); - Collection<Key<?>> keys = VmClassStatDAO.vmClassStatsCategory.getKeys(); - assertTrue(keys.contains(new Key<Integer>("vm-id", false))); - assertTrue(keys.contains(new Key<Long>("timestamp", false))); - assertTrue(keys.contains(new Key<Long>("loadedClasses", false))); - assertEquals(3, keys.size()); - - } -}