Mercurial > hg > release > thermostat-0.5
changeset 832:99a6f56a52b1
Merge
author | Roman Kennke <rkennke@redhat.com> |
---|---|
date | Mon, 10 Dec 2012 15:05:37 +0100 |
parents | 6cd46e3563ce (current diff) 4a2c3aa5742e (diff) |
children | 941737272b79 |
files | agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/ServiceCommand.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/StorageCommand.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/AgentApplicationTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/DBServiceTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/ThermostatServiceTest.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ServerHandler.java client/memory-stats-panel/core/pom.xml client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryMeter.java client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsController.java client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsService.java client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsView.java client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsViewProvider.java client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/Payload.java client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/RangeModel.java client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/StatsModel.java client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/locale/LocaleResources.java client/memory-stats-panel/core/src/main/resources/com/redhat/thermostat/client/stats/memory/locale/strings.properties client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsControllerTest.java client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/PayloadTest.java client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/RangeModelTest.java client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/StatsModelTest.java client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/locale/TranslateTest.java client/memory-stats-panel/pom.xml client/memory-stats-panel/swing/pom.xml client/memory-stats-panel/swing/src/main/java/com/redhat/thermostat/client/stats/memory/swing/MemoryGraphPanel.java client/memory-stats-panel/swing/src/main/java/com/redhat/thermostat/client/stats/memory/swing/MemoryStatsPanelActivator.java client/memory-stats-panel/swing/src/main/java/com/redhat/thermostat/client/stats/memory/swing/MemoryStatsViewImpl.java client/memory-stats-panel/swing/src/main/java/com/redhat/thermostat/client/stats/memory/swing/SwingMemoryStatsViewProvider.java client/memory-stats-panel/swing/src/main/resources/com/redhat/thermostat/client/stats/memory/locale/strings.properties client/memory-stats-panel/swing/src/test/java/com/redhat/thermostat/client/stats/memory/swing/MemoryStatsPanelActivatorTest.java common/command/src/main/java/com/redhat/thermostat/common/command/Response.java distribution/config/commands/gui.properties distribution/pom.xml pom.xml web/client/src/main/java/com/redhat/thermostat/web/client/Activator.java web/client/src/main/java/com/redhat/thermostat/web/client/WebCursor.java web/client/src/main/java/com/redhat/thermostat/web/client/WebStorage.java web/client/src/main/java/com/redhat/thermostat/web/client/WebStorageProvider.java web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java web/client/src/test/java/com/redhat/thermostat/web/client/TestObj.java web/client/src/test/java/com/redhat/thermostat/web/client/WebStorageTest.java web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java |
diffstat | 135 files changed, 6430 insertions(+), 5121 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Mon Dec 10 14:30:36 2012 +0100 +++ b/.hgignore Mon Dec 10 15:05:37 2012 +0100 @@ -11,3 +11,5 @@ target/* bin/* *~ +nbactions*.xml +nb-configuration.xml
--- a/Makefile Mon Dec 10 14:30:36 2012 +0100 +++ b/Makefile Mon Dec 10 15:05:37 2012 +0100 @@ -11,7 +11,7 @@ # Do not change anything below # REPO_FLAG = -Dmaven.repo.local=$(REPO_LOC) -GOAL = integration-test +GOAL = package POM = pom.xml ifeq ($(SKIP_TESTS),true)
--- a/agent/cli/pom.xml Mon Dec 10 14:30:36 2012 +0100 +++ b/agent/cli/pom.xml Mon Dec 10 15:05:37 2012 +0100 @@ -98,9 +98,6 @@ <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> <Bundle-Activator>com.redhat.thermostat.agent.cli.impl.Activator</Bundle-Activator> <Bundle-SymbolicName>com.redhat.thermostat.agent.cli</Bundle-SymbolicName> - <Export-Package> - com.redhat.thermostat.agent.cli, - </Export-Package> <Private-Package> com.redhat.thermostat.agent.cli.impl, com.redhat.thermostat.agent.cli.impl.locale,
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,252 +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.agent.cli; - -import java.util.concurrent.CountDownLatch; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; - -import sun.misc.Signal; -import sun.misc.SignalHandler; - -import com.redhat.thermostat.agent.Agent; -import com.redhat.thermostat.agent.command.ConfigurationServer; -import com.redhat.thermostat.agent.config.AgentConfigsUtils; -import com.redhat.thermostat.agent.config.AgentOptionParser; -import com.redhat.thermostat.agent.config.AgentStartupConfiguration; -import com.redhat.thermostat.backend.BackendRegistry; -import com.redhat.thermostat.backend.BackendService; -import com.redhat.thermostat.common.Constants; -import com.redhat.thermostat.common.LaunchException; -import com.redhat.thermostat.common.cli.Arguments; -import com.redhat.thermostat.common.cli.CommandContext; -import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.config.InvalidConfigurationException; -import com.redhat.thermostat.common.dao.DAOFactory; -import com.redhat.thermostat.common.dao.DAOFactoryImpl; -import com.redhat.thermostat.common.tools.BasicCommand; -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.storage.core.Connection; -import com.redhat.thermostat.storage.core.StorageProvider; -import com.redhat.thermostat.storage.core.StorageProviderUtil; -import com.redhat.thermostat.storage.core.Connection.ConnectionListener; -import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; - -@SuppressWarnings("restriction") -public final class AgentApplication extends BasicCommand { - - private static final String NAME = "agent"; - - private final BundleContext bundleContext; - private final ConfigurationCreator configurationCreator; - private final DAOFactoryCreator daoFactoryCreator; - - private AgentStartupConfiguration configuration; - private AgentOptionParser parser; - - public AgentApplication(BundleContext bundleContext) { - this(bundleContext, new ConfigurationCreator(), new DAOFactoryCreator()); - } - - AgentApplication(BundleContext bundleContext, ConfigurationCreator configurationCreator, DAOFactoryCreator daoFactoryCreator) { - this.bundleContext = bundleContext; - this.configurationCreator = configurationCreator; - this.daoFactoryCreator = daoFactoryCreator; - } - - private void parseArguments(Arguments args) throws InvalidConfigurationException { - parser = new AgentOptionParser(configuration, args); - parser.parse(); - } - - @Override - public AgentStartupConfiguration getConfiguration() { - return configuration; - } - - private void runAgent(CommandContext ctx) { - long startTime = System.currentTimeMillis(); - configuration.setStartTime(startTime); - - if (configuration.isDebugConsole()) { - LoggingUtils.useDevelConsole(); - } - final Logger logger = LoggingUtils.getLogger(AgentApplication.class); - - final DAOFactory daoFactory = daoFactoryCreator.create(configuration); - - Connection connection = daoFactory.getConnection(); - ConnectionListener connectionListener = new ConnectionListener() { - @Override - public void changed(ConnectionStatus newStatus) { - switch (newStatus) { - case DISCONNECTED: - logger.warning("Unexpected disconnect event."); - break; - case CONNECTING: - logger.fine("Connecting to storage."); - break; - case CONNECTED: - logger.fine("Connected to storage, registering storage as service"); - daoFactory.registerDAOsAndStorageAsOSGiServices(); - break; - case FAILED_TO_CONNECT: - logger.warning("Could not connect to storage."); - System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE); - default: - logger.warning("Unfamiliar ConnectionStatus value"); - } - } - }; - - connection.addListener(connectionListener); - connection.connect(); - logger.fine("Connecting to storage..."); - - ServiceReference configServiceRef = bundleContext.getServiceReference(ConfigurationServer.class.getName()); - final ConfigurationServer configServer = (ConfigurationServer) bundleContext.getService(configServiceRef); - configServer.startListening(configuration.getConfigListenAddress()); - - BackendRegistry backendRegistry = null; - try { - backendRegistry = new BackendRegistry(bundleContext); - - } catch (Exception e) { - logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", e); - System.exit(Constants.EXIT_BACKEND_LOAD_ERROR); - } - - final Agent agent = new Agent(backendRegistry, configuration, daoFactory); - try { - logger.fine("Starting agent."); - agent.start(); - - bundleContext.registerService(BackendService.class, new BackendService(), null); - - } catch (LaunchException le) { - logger.log(Level.SEVERE, - "Agent could not start, probably because a configured backend could not be activated.", - le); - System.exit(Constants.EXIT_BACKEND_START_ERROR); - } - logger.fine("Agent started."); - - ctx.getConsole().getOutput().println("Agent id: " + agent.getId()); - ctx.getConsole().getOutput().println("agent started."); - logger.fine("Agent id: " + agent.getId()); - - final CountDownLatch shutdownLatch = new CountDownLatch(1); - SignalHandler handler = new CustomSignalHandler(agent, configServer, logger, shutdownLatch); - Signal.handle(new Signal("INT"), handler); - Signal.handle(new Signal("TERM"), handler); - try { - // Wait for either SIGINT or SIGTERM - shutdownLatch.await(); - logger.fine("terminating agent cmd"); - } catch (InterruptedException e) { - return; - } - } - - @Override - public void run(CommandContext ctx) throws CommandException { - try { - configuration = configurationCreator.create(); - - parseArguments(ctx.getArguments()); - if (!parser.isHelp()) { - runAgent(ctx); - } - } catch (InvalidConfigurationException ex) { - throw new CommandException(ex); - } - } - - @Override - public String getName() { - return NAME; - } - - // Does not need a reference of the enclosing type so lets declare this class static - private static class CustomSignalHandler implements SignalHandler { - - private Agent agent; - private ConfigurationServer configServer; - private Logger logger; - private CountDownLatch shutdownLatch; - - CustomSignalHandler(Agent agent, ConfigurationServer configServer, Logger logger, CountDownLatch latch) { - this.agent = agent; - this.configServer = configServer; - this.logger = logger; - this.shutdownLatch = latch; - } - - @Override - public void handle(Signal arg0) { - configServer.stopListening(); - try { - agent.stop(); - } catch (Exception ex) { - // We don't want any exception to hold back the signal handler, otherwise - // there will be no way to actually stop Thermostat. - ex.printStackTrace(); - } - logger.fine("Agent stopped."); - shutdownLatch.countDown(); - } - - } - - static class ConfigurationCreator { - public AgentStartupConfiguration create() throws InvalidConfigurationException { - return AgentConfigsUtils.createAgentConfigs(); - } - } - - static class DAOFactoryCreator { - public DAOFactory create(AgentStartupConfiguration config) { - StorageProvider connProv = StorageProviderUtil.getStorageProvider(config); - final DAOFactory daoFactory = new DAOFactoryImpl(connProv); - return daoFactory; - } - } - -}
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/ServiceCommand.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +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.agent.cli; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Semaphore; - -import com.redhat.thermostat.agent.cli.db.StorageAlreadyRunningException; -import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; -import com.redhat.thermostat.common.ActionEvent; -import com.redhat.thermostat.common.ActionListener; -import com.redhat.thermostat.common.cli.CommandContext; -import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.cli.SimpleCommand; -import com.redhat.thermostat.common.locale.Translate; -import com.redhat.thermostat.common.tools.ApplicationState; -import com.redhat.thermostat.common.utils.OSGIUtils; -import com.redhat.thermostat.launcher.Launcher; - -/** - * Simple service that allows starting Agent and DB Backend - * in a single step. - */ -public class ServiceCommand extends SimpleCommand implements ActionListener<ApplicationState> { - - private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); - - private static final String NAME = "service"; - - private List<ActionListener<ApplicationState>> listeners; - - private Semaphore agentBarrier = new Semaphore(0); - - public ServiceCommand() { - listeners = new ArrayList<>(); - listeners.add(this); - } - - @Override - public void run(CommandContext ctx) throws CommandException { - Launcher launcher = getLauncher(); - String[] storageStartArgs = new String[] { "storage", "--start" }; - launcher.setArgs(storageStartArgs); - launcher.run(listeners); - agentBarrier.acquireUninterruptibly(); - String[] storageStopArgs = new String[] { "storage", "--stop" }; - launcher.setArgs(storageStopArgs); - launcher.run(); - } - - @Override - public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { - if (actionEvent.getSource() instanceof StorageCommand) { - StorageCommand storage = (StorageCommand) actionEvent.getSource(); - // Implementation detail: there is a single StorageCommand instance registered - // as an OSGi service. We remove ourselves as listener so that we don't get - // notified in the case that the command is invoked by some other means later. - storage.getNotifier().removeActionListener(this); - switch (actionEvent.getActionId()) { - case START: - String dbUrl = storage.getConfiguration().getDBConnectionString(); - Launcher launcher = getLauncher(); - String[] agentArgs = new String[] {"agent", "-d", dbUrl}; - System.err.println(translator.localize(LocaleResources.STARTING_AGENT)); - launcher.setArgs(agentArgs); - launcher.run(); - agentBarrier.release(); - break; - case FAIL: - System.err.println(translator.localize(LocaleResources.ERROR_STARTING_DB)); - Object payload = actionEvent.getPayload(); - if (payload instanceof StorageAlreadyRunningException) { - System.err.println(translator.localize(LocaleResources.STORAGE_ALREADY_RUNNING)); - } - break; - } - } - } - - @Override - public String getName() { - return NAME; - } - - @Override - public boolean isAvailableInShell() { - return false; - } - - @Override - public boolean isStorageRequired() { - return false; - } - - private Launcher getLauncher() { - OSGIUtils osgi = OSGIUtils.getInstance(); - return osgi.getService(Launcher.class); - } - -}
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/StorageCommand.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,184 +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.agent.cli; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Properties; - -import com.redhat.thermostat.agent.cli.db.DBConfig; -import com.redhat.thermostat.agent.cli.db.DBOptionParser; -import com.redhat.thermostat.agent.cli.db.DBStartupConfiguration; -import com.redhat.thermostat.agent.cli.db.MongoProcessRunner; -import com.redhat.thermostat.common.cli.Arguments; -import com.redhat.thermostat.common.cli.CommandContext; -import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.config.ConfigUtils; -import com.redhat.thermostat.common.config.InvalidConfigurationException; -import com.redhat.thermostat.common.tools.ApplicationException; -import com.redhat.thermostat.common.tools.ApplicationState; -import com.redhat.thermostat.common.tools.BasicCommand; - -public class StorageCommand extends BasicCommand { - - private static final String NAME = "storage"; - - private DBStartupConfiguration configuration; - private DBOptionParser parser; - - private MongoProcessRunner runner; - - private void parseArguments(Arguments args) throws InvalidConfigurationException { - - this.configuration = new DBStartupConfiguration(); - // configs, read everything that is in the configs - File propertyFile = ConfigUtils.getStorageConfigurationFile(); - if (!propertyFile.exists()) { - throw new InvalidConfigurationException("can't access database configuration file " + - propertyFile); - } - readAndSetProperties(propertyFile); - - parser = new DBOptionParser(configuration, args); - parser.parse(); - } - - @Override - public void run(CommandContext ctx) throws CommandException { - - try { - parseArgsAndRun(ctx); - } catch (InvalidConfigurationException e) { - throw new CommandException(e); - } - } - - private void parseArgsAndRun(CommandContext ctx) - throws InvalidConfigurationException { - parseArguments(ctx.getArguments()); - - // dry run means we don't do anything at all - if (parser.isDryRun()) return; - - runner = createRunner(); - try { - switch (parser.getAction()) { - case START: - startService(); - break; - case STOP: - stopService(); - break; - default: - break; - } - getNotifier().fireAction(ApplicationState.SUCCESS); - } catch (Exception e) { - getNotifier().fireAction(ApplicationState.FAIL, e); - } - } - - private void readAndSetProperties(File propertyFile) throws InvalidConfigurationException { - - Properties properties = new Properties(); - try { - properties.load(new FileInputStream(propertyFile)); - - } catch (IOException e) { - throw new InvalidConfigurationException(e); - } - - if (properties.containsKey(DBConfig.PORT.name())) { - String port = (String) properties.get(DBConfig.PORT.name()); - int localPort = Integer.parseInt(port); - configuration.setPort(localPort); - } else { - throw new InvalidConfigurationException(DBConfig.PORT + " property missing"); - } - - if (properties.containsKey(DBConfig.PROTOCOL.name())) { - String url = (String) properties.get(DBConfig.PROTOCOL.name()); - configuration.setProtocol(url); - } else { - throw new InvalidConfigurationException(DBConfig.PROTOCOL + " property missing"); - } - - if (properties.containsKey(DBConfig.BIND.name())) { - String ip = (String) properties.get(DBConfig.BIND.name()); - configuration.setBindIP(ip); - } else { - throw new InvalidConfigurationException(DBConfig.BIND + " property missing"); - } - } - - private void startService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { - runner.startService(); - getNotifier().fireAction(ApplicationState.START); - } - - - private void stopService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { - check(); - runner.stopService(); - getNotifier().fireAction(ApplicationState.STOP); - } - - MongoProcessRunner createRunner() { - return new MongoProcessRunner(configuration, parser.isQuiet()); - } - - private void check() throws InvalidConfigurationException { - if (!configuration.getDBPath().exists() || - !configuration.getLogFile().getParentFile().exists() || - !configuration.getPidFile().getParentFile().exists()) - { - throw new InvalidConfigurationException("database directories do not exist..."); - } - } - - @Override - public DBStartupConfiguration getConfiguration() { - return configuration; - } - - @Override - public String getName() { - return NAME; - } - -}
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/db/DBConfig.java Mon Dec 10 14:30:36 2012 +0100 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/db/DBConfig.java Mon Dec 10 15:05:37 2012 +0100 @@ -36,7 +36,7 @@ package com.redhat.thermostat.agent.cli.db; -import com.redhat.thermostat.agent.cli.StorageCommand; +import com.redhat.thermostat.agent.cli.impl.StorageCommand; /** * Set of configuration option that the {@link StorageCommand} understands.
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java Mon Dec 10 14:30:36 2012 +0100 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java Mon Dec 10 15:05:37 2012 +0100 @@ -41,9 +41,6 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; -import com.redhat.thermostat.agent.cli.AgentApplication; -import com.redhat.thermostat.agent.cli.StorageCommand; -import com.redhat.thermostat.agent.cli.ServiceCommand; import com.redhat.thermostat.common.cli.CommandRegistry; import com.redhat.thermostat.common.cli.CommandRegistryImpl;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/AgentApplication.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,252 @@ +/* + * 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.agent.cli.impl; + +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +import sun.misc.Signal; +import sun.misc.SignalHandler; + +import com.redhat.thermostat.agent.Agent; +import com.redhat.thermostat.agent.command.ConfigurationServer; +import com.redhat.thermostat.agent.config.AgentConfigsUtils; +import com.redhat.thermostat.agent.config.AgentOptionParser; +import com.redhat.thermostat.agent.config.AgentStartupConfiguration; +import com.redhat.thermostat.backend.BackendRegistry; +import com.redhat.thermostat.backend.BackendService; +import com.redhat.thermostat.common.Constants; +import com.redhat.thermostat.common.LaunchException; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.config.InvalidConfigurationException; +import com.redhat.thermostat.common.dao.DAOFactory; +import com.redhat.thermostat.common.dao.DAOFactoryImpl; +import com.redhat.thermostat.common.tools.BasicCommand; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.storage.core.Connection; +import com.redhat.thermostat.storage.core.StorageProvider; +import com.redhat.thermostat.storage.core.StorageProviderUtil; +import com.redhat.thermostat.storage.core.Connection.ConnectionListener; +import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; + +@SuppressWarnings("restriction") +public final class AgentApplication extends BasicCommand { + + private static final String NAME = "agent"; + + private final BundleContext bundleContext; + private final ConfigurationCreator configurationCreator; + private final DAOFactoryCreator daoFactoryCreator; + + private AgentStartupConfiguration configuration; + private AgentOptionParser parser; + + public AgentApplication(BundleContext bundleContext) { + this(bundleContext, new ConfigurationCreator(), new DAOFactoryCreator()); + } + + AgentApplication(BundleContext bundleContext, ConfigurationCreator configurationCreator, DAOFactoryCreator daoFactoryCreator) { + this.bundleContext = bundleContext; + this.configurationCreator = configurationCreator; + this.daoFactoryCreator = daoFactoryCreator; + } + + private void parseArguments(Arguments args) throws InvalidConfigurationException { + parser = new AgentOptionParser(configuration, args); + parser.parse(); + } + + @Override + public AgentStartupConfiguration getConfiguration() { + return configuration; + } + + private void runAgent(CommandContext ctx) { + long startTime = System.currentTimeMillis(); + configuration.setStartTime(startTime); + + if (configuration.isDebugConsole()) { + LoggingUtils.useDevelConsole(); + } + final Logger logger = LoggingUtils.getLogger(AgentApplication.class); + + final DAOFactory daoFactory = daoFactoryCreator.create(configuration); + + Connection connection = daoFactory.getConnection(); + ConnectionListener connectionListener = new ConnectionListener() { + @Override + public void changed(ConnectionStatus newStatus) { + switch (newStatus) { + case DISCONNECTED: + logger.warning("Unexpected disconnect event."); + break; + case CONNECTING: + logger.fine("Connecting to storage."); + break; + case CONNECTED: + logger.fine("Connected to storage, registering storage as service"); + daoFactory.registerDAOsAndStorageAsOSGiServices(); + break; + case FAILED_TO_CONNECT: + logger.warning("Could not connect to storage."); + System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE); + default: + logger.warning("Unfamiliar ConnectionStatus value"); + } + } + }; + + connection.addListener(connectionListener); + connection.connect(); + logger.fine("Connecting to storage..."); + + ServiceReference configServiceRef = bundleContext.getServiceReference(ConfigurationServer.class.getName()); + final ConfigurationServer configServer = (ConfigurationServer) bundleContext.getService(configServiceRef); + configServer.startListening(configuration.getConfigListenAddress()); + + BackendRegistry backendRegistry = null; + try { + backendRegistry = new BackendRegistry(bundleContext); + + } catch (Exception e) { + logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", e); + System.exit(Constants.EXIT_BACKEND_LOAD_ERROR); + } + + final Agent agent = new Agent(backendRegistry, configuration, daoFactory); + try { + logger.fine("Starting agent."); + agent.start(); + + bundleContext.registerService(BackendService.class, new BackendService(), null); + + } catch (LaunchException le) { + logger.log(Level.SEVERE, + "Agent could not start, probably because a configured backend could not be activated.", + le); + System.exit(Constants.EXIT_BACKEND_START_ERROR); + } + logger.fine("Agent started."); + + ctx.getConsole().getOutput().println("Agent id: " + agent.getId()); + ctx.getConsole().getOutput().println("agent started."); + logger.fine("Agent id: " + agent.getId()); + + final CountDownLatch shutdownLatch = new CountDownLatch(1); + SignalHandler handler = new CustomSignalHandler(agent, configServer, logger, shutdownLatch); + Signal.handle(new Signal("INT"), handler); + Signal.handle(new Signal("TERM"), handler); + try { + // Wait for either SIGINT or SIGTERM + shutdownLatch.await(); + logger.fine("terminating agent cmd"); + } catch (InterruptedException e) { + return; + } + } + + @Override + public void run(CommandContext ctx) throws CommandException { + try { + configuration = configurationCreator.create(); + + parseArguments(ctx.getArguments()); + if (!parser.isHelp()) { + runAgent(ctx); + } + } catch (InvalidConfigurationException ex) { + throw new CommandException(ex); + } + } + + @Override + public String getName() { + return NAME; + } + + // Does not need a reference of the enclosing type so lets declare this class static + private static class CustomSignalHandler implements SignalHandler { + + private Agent agent; + private ConfigurationServer configServer; + private Logger logger; + private CountDownLatch shutdownLatch; + + CustomSignalHandler(Agent agent, ConfigurationServer configServer, Logger logger, CountDownLatch latch) { + this.agent = agent; + this.configServer = configServer; + this.logger = logger; + this.shutdownLatch = latch; + } + + @Override + public void handle(Signal arg0) { + configServer.stopListening(); + try { + agent.stop(); + } catch (Exception ex) { + // We don't want any exception to hold back the signal handler, otherwise + // there will be no way to actually stop Thermostat. + ex.printStackTrace(); + } + logger.fine("Agent stopped."); + shutdownLatch.countDown(); + } + + } + + static class ConfigurationCreator { + public AgentStartupConfiguration create() throws InvalidConfigurationException { + return AgentConfigsUtils.createAgentConfigs(); + } + } + + static class DAOFactoryCreator { + public DAOFactory create(AgentStartupConfiguration config) { + StorageProvider connProv = StorageProviderUtil.getStorageProvider(config); + final DAOFactory daoFactory = new DAOFactoryImpl(connProv); + return daoFactory; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/ServiceCommand.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,135 @@ +/* + * 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.agent.cli.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Semaphore; + +import com.redhat.thermostat.agent.cli.db.StorageAlreadyRunningException; +import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.SimpleCommand; +import com.redhat.thermostat.common.locale.Translate; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.common.utils.OSGIUtils; +import com.redhat.thermostat.launcher.Launcher; + +/** + * Simple service that allows starting Agent and DB Backend + * in a single step. + */ +public class ServiceCommand extends SimpleCommand implements ActionListener<ApplicationState> { + + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + + private static final String NAME = "service"; + + private List<ActionListener<ApplicationState>> listeners; + + private Semaphore agentBarrier = new Semaphore(0); + + public ServiceCommand() { + listeners = new ArrayList<>(); + listeners.add(this); + } + + @Override + public void run(CommandContext ctx) throws CommandException { + Launcher launcher = getLauncher(); + String[] storageStartArgs = new String[] { "storage", "--start" }; + launcher.setArgs(storageStartArgs); + launcher.run(listeners); + agentBarrier.acquireUninterruptibly(); + String[] storageStopArgs = new String[] { "storage", "--stop" }; + launcher.setArgs(storageStopArgs); + launcher.run(); + } + + @Override + public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { + if (actionEvent.getSource() instanceof StorageCommand) { + StorageCommand storage = (StorageCommand) actionEvent.getSource(); + // Implementation detail: there is a single StorageCommand instance registered + // as an OSGi service. We remove ourselves as listener so that we don't get + // notified in the case that the command is invoked by some other means later. + storage.getNotifier().removeActionListener(this); + switch (actionEvent.getActionId()) { + case START: + String dbUrl = storage.getConfiguration().getDBConnectionString(); + Launcher launcher = getLauncher(); + String[] agentArgs = new String[] {"agent", "-d", dbUrl}; + System.err.println(translator.localize(LocaleResources.STARTING_AGENT)); + launcher.setArgs(agentArgs); + launcher.run(); + agentBarrier.release(); + break; + case FAIL: + System.err.println(translator.localize(LocaleResources.ERROR_STARTING_DB)); + Object payload = actionEvent.getPayload(); + if (payload instanceof StorageAlreadyRunningException) { + System.err.println(translator.localize(LocaleResources.STORAGE_ALREADY_RUNNING)); + } + break; + } + } + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean isAvailableInShell() { + return false; + } + + @Override + public boolean isStorageRequired() { + return false; + } + + private Launcher getLauncher() { + OSGIUtils osgi = OSGIUtils.getInstance(); + return osgi.getService(Launcher.class); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/StorageCommand.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,184 @@ +/* + * 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.agent.cli.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +import com.redhat.thermostat.agent.cli.db.DBConfig; +import com.redhat.thermostat.agent.cli.db.DBOptionParser; +import com.redhat.thermostat.agent.cli.db.DBStartupConfiguration; +import com.redhat.thermostat.agent.cli.db.MongoProcessRunner; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.config.ConfigUtils; +import com.redhat.thermostat.common.config.InvalidConfigurationException; +import com.redhat.thermostat.common.tools.ApplicationException; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.common.tools.BasicCommand; + +public class StorageCommand extends BasicCommand { + + private static final String NAME = "storage"; + + private DBStartupConfiguration configuration; + private DBOptionParser parser; + + private MongoProcessRunner runner; + + private void parseArguments(Arguments args) throws InvalidConfigurationException { + + this.configuration = new DBStartupConfiguration(); + // configs, read everything that is in the configs + File propertyFile = ConfigUtils.getStorageConfigurationFile(); + if (!propertyFile.exists()) { + throw new InvalidConfigurationException("can't access database configuration file " + + propertyFile); + } + readAndSetProperties(propertyFile); + + parser = new DBOptionParser(configuration, args); + parser.parse(); + } + + @Override + public void run(CommandContext ctx) throws CommandException { + + try { + parseArgsAndRun(ctx); + } catch (InvalidConfigurationException e) { + throw new CommandException(e); + } + } + + private void parseArgsAndRun(CommandContext ctx) + throws InvalidConfigurationException { + parseArguments(ctx.getArguments()); + + // dry run means we don't do anything at all + if (parser.isDryRun()) return; + + runner = createRunner(); + try { + switch (parser.getAction()) { + case START: + startService(); + break; + case STOP: + stopService(); + break; + default: + break; + } + getNotifier().fireAction(ApplicationState.SUCCESS); + } catch (Exception e) { + getNotifier().fireAction(ApplicationState.FAIL, e); + } + } + + private void readAndSetProperties(File propertyFile) throws InvalidConfigurationException { + + Properties properties = new Properties(); + try { + properties.load(new FileInputStream(propertyFile)); + + } catch (IOException e) { + throw new InvalidConfigurationException(e); + } + + if (properties.containsKey(DBConfig.PORT.name())) { + String port = (String) properties.get(DBConfig.PORT.name()); + int localPort = Integer.parseInt(port); + configuration.setPort(localPort); + } else { + throw new InvalidConfigurationException(DBConfig.PORT + " property missing"); + } + + if (properties.containsKey(DBConfig.PROTOCOL.name())) { + String url = (String) properties.get(DBConfig.PROTOCOL.name()); + configuration.setProtocol(url); + } else { + throw new InvalidConfigurationException(DBConfig.PROTOCOL + " property missing"); + } + + if (properties.containsKey(DBConfig.BIND.name())) { + String ip = (String) properties.get(DBConfig.BIND.name()); + configuration.setBindIP(ip); + } else { + throw new InvalidConfigurationException(DBConfig.BIND + " property missing"); + } + } + + private void startService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { + runner.startService(); + getNotifier().fireAction(ApplicationState.START); + } + + + private void stopService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { + check(); + runner.stopService(); + getNotifier().fireAction(ApplicationState.STOP); + } + + MongoProcessRunner createRunner() { + return new MongoProcessRunner(configuration, parser.isQuiet()); + } + + private void check() throws InvalidConfigurationException { + if (!configuration.getDBPath().exists() || + !configuration.getLogFile().getParentFile().exists() || + !configuration.getPidFile().getParentFile().exists()) + { + throw new InvalidConfigurationException("database directories do not exist..."); + } + } + + @Override + public DBStartupConfiguration getConfiguration() { + return configuration; + } + + @Override + public String getName() { + return NAME; + } + +}
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/AgentApplicationTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +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.agent.cli; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import com.redhat.thermostat.agent.cli.AgentApplication.ConfigurationCreator; -import com.redhat.thermostat.agent.cli.AgentApplication.DAOFactoryCreator; -import com.redhat.thermostat.agent.config.AgentStartupConfiguration; -import com.redhat.thermostat.common.cli.Arguments; -import com.redhat.thermostat.common.cli.CommandContext; -import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.config.InvalidConfigurationException; -import com.redhat.thermostat.common.dao.DAOFactory; -import com.redhat.thermostat.storage.core.Connection; -import com.redhat.thermostat.storage.core.Connection.ConnectionListener; -import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; -import com.redhat.thermostat.test.StubBundleContext; - -public class AgentApplicationTest { - - // TODO: Test i18nized versions when they come. - - private StubBundleContext context; - private DAOFactory daoFactory; - private Connection connection; - - private AgentApplication agent; - - private ArgumentCaptor<ConnectionListener> listenerCaptor; - - @Before - public void setUp() throws InvalidConfigurationException { - - context = new StubBundleContext(); - - AgentStartupConfiguration config = mock(AgentStartupConfiguration.class); - when(config.getDBConnectionString()).thenReturn("test string; please ignore"); - - ConfigurationCreator configCreator = mock(ConfigurationCreator.class); - when(configCreator.create()).thenReturn(config); - - listenerCaptor = ArgumentCaptor.forClass(ConnectionListener.class); - - connection = mock(Connection.class); - doNothing().when(connection).addListener(listenerCaptor.capture()); - - daoFactory = mock(DAOFactory.class); - when(daoFactory.getConnection()).thenReturn(connection); - - DAOFactoryCreator daoCreator = mock(DAOFactoryCreator.class); - when(daoCreator.create(config)).thenReturn(daoFactory); - - agent = new AgentApplication(context, configCreator, daoCreator); - } - - @After - public void tearDown() { - agent = null; - } - - @Test - public void testName() { - String name = agent.getName(); - assertEquals("agent", name); - } - - @Test - public void testDescAndUsage() { - assertNotNull(agent.getDescription()); - assertNotNull(agent.getUsage()); - } - - @Test - public void testAgentStartupRegistersDAOs() throws CommandException { - - doThrow(new ThatsAllThatWeCareAbout()).when(connection).connect(); - - Arguments args = mock(Arguments.class); - CommandContext commandContext = mock(CommandContext.class); - - when(commandContext.getArguments()).thenReturn(args); - - try { - agent.run(commandContext); - fail("not supposed to get here"); - } catch (ThatsAllThatWeCareAbout done) { - // agent.run() is so convoluted that we just want to test a part of - // it - } - - ConnectionListener listener = listenerCaptor.getValue(); - listener.changed(ConnectionStatus.CONNECTED); - - verify(daoFactory).registerDAOsAndStorageAsOSGiServices(); - } - - // FIXME test the rest of AgentApplication - - private static class ThatsAllThatWeCareAbout extends RuntimeException { - - } -}
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/DBServiceTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,296 +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.agent.cli; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Collection; -import java.util.Properties; -import java.util.Random; -import java.util.concurrent.CountDownLatch; - -import junit.framework.Assert; - -import org.apache.commons.cli.Option; -import org.apache.commons.cli.OptionGroup; -import org.apache.commons.cli.Options; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import com.redhat.thermostat.agent.cli.StorageCommand; -import com.redhat.thermostat.agent.cli.db.DBConfig; -import com.redhat.thermostat.agent.cli.db.DBStartupConfiguration; -import com.redhat.thermostat.agent.cli.db.MongoProcessRunner; -import com.redhat.thermostat.common.ActionEvent; -import com.redhat.thermostat.common.ActionListener; -import com.redhat.thermostat.common.cli.CommandContext; -import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.cli.SimpleArguments; -import com.redhat.thermostat.common.config.InvalidConfigurationException; -import com.redhat.thermostat.common.tools.ApplicationException; -import com.redhat.thermostat.common.tools.ApplicationState; - -public class DBServiceTest { - - private static final String PORT = "27518"; - private static final String BIND = "127.0.0.1"; - private static final String PROTOCOL = "mongodb"; - private static final String DB = "storage/db"; - - private String tmpDir; - - @Before - public void setup() { - // need to create a dummy config file for the test - try { - Random random = new Random(); - - tmpDir = System.getProperty("java.io.tmpdir") + File.separatorChar + - Math.abs(random.nextInt()) + File.separatorChar; - - System.setProperty("THERMOSTAT_HOME", tmpDir); - File base = new File(tmpDir + "storage"); - base.mkdirs(); - - File tmpConfigs = new File(base, "db.properties"); - - new File(base, "run").mkdirs(); - new File(base, "logs").mkdirs(); - new File(base, "db").mkdirs(); - - Properties props = new Properties(); - - props.setProperty(DBConfig.BIND.name(), BIND); - props.setProperty(DBConfig.PORT.name(), PORT); - props.setProperty(DBConfig.PROTOCOL.name(), PROTOCOL); - - props.store(new FileOutputStream(tmpConfigs), "thermostat test properties"); - - } catch (IOException e) { - Assert.fail("cannot setup tests: " + e); - } - } - - @Test - public void testConfig() throws CommandException { - SimpleArguments args = new SimpleArguments(); - args.addArgument("quiet", null); - args.addArgument("start", null); - args.addArgument("dryRun", null); - CommandContext ctx = mock(CommandContext.class); - when(ctx.getArguments()).thenReturn(args); - - StorageCommand service = new StorageCommand() { - @Override - MongoProcessRunner createRunner() { - throw new AssertionError("dry run should never create an actual runner"); - } - }; - - service.run(ctx); - - DBStartupConfiguration conf = service.getConfiguration(); - - Assert.assertEquals(tmpDir + DB, conf.getDBPath().getPath()); - Assert.assertEquals(Integer.parseInt(PORT), conf.getPort()); - Assert.assertEquals(PROTOCOL, conf.getProtocol()); - } - - private StorageCommand prepareService(boolean startSuccess) throws IOException, - InterruptedException, InvalidConfigurationException, ApplicationException - { - final MongoProcessRunner runner = mock(MongoProcessRunner.class); - if (!startSuccess) { - doThrow(new ApplicationException("mock exception")).when(runner).startService(); - } - - // TODO: stop not tested yet, but be sure it's not called from the code - doThrow(new ApplicationException("mock exception")).when(runner).stopService(); - - StorageCommand service = new StorageCommand() { - @Override - MongoProcessRunner createRunner() { - return runner; - } - }; - - return service; - } - - @Test - public void testListeners() throws InterruptedException, IOException, ApplicationException, InvalidConfigurationException, CommandException - { - StorageCommand service = prepareService(true); - - final CountDownLatch latch = new CountDownLatch(2); - - final boolean[] result = new boolean[2]; - service.getNotifier().addActionListener(new ActionListener<ApplicationState>() { - @Override - public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { - switch (actionEvent.getActionId()) { - case FAIL: - result[0] = false; - latch.countDown(); - latch.countDown(); - break; - - case SUCCESS: - result[0] = true; - latch.countDown(); - break; - - case START: - result[1] = true; - latch.countDown(); - break; - } - } - }); - - service.run(prepareContext()); - latch.await(); - - Assert.assertTrue(result[0]); - Assert.assertTrue(result[1]); - } - - @Test - public void testListenersFail() throws InterruptedException, IOException, ApplicationException, CommandException, InvalidConfigurationException - { - StorageCommand service = prepareService(false); - - final CountDownLatch latch = new CountDownLatch(1); - final boolean[] result = new boolean[1]; - service.getNotifier().addActionListener(new ActionListener<ApplicationState>() { - @Override - public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { - switch (actionEvent.getActionId()) { - case FAIL: - result[0] = true; - break; - - case SUCCESS: - result[0] = false; - break; - } - latch.countDown(); - } - }); - - service.run(prepareContext()); - latch.await(); - - Assert.assertTrue(result[0]); - } - - private CommandContext prepareContext() { - SimpleArguments args = new SimpleArguments(); - args.addArgument("quiet", "--quiet"); - args.addArgument("start", "--start"); - CommandContext ctx = mock(CommandContext.class); - when(ctx.getArguments()).thenReturn(args); - return ctx; - } - - @Test - public void testName() { - StorageCommand dbService = new StorageCommand(); - String name = dbService.getName(); - assertEquals("storage", name); - } - - @Test - public void testDescAndUsage() { - StorageCommand dbService = new StorageCommand(); - assertNotNull(dbService.getDescription()); - assertNotNull(dbService.getUsage()); - } - - @Ignore - @Test - public void testOptions() { - StorageCommand dbService = new StorageCommand(); - Options options = dbService.getOptions(); - assertNotNull(options); - assertEquals(4, options.getOptions().size()); - - assertTrue(options.hasOption("dryRun")); - Option dry = options.getOption("dryRun"); - assertEquals("d", dry.getOpt()); - assertEquals("run the service in dry run mode", dry.getDescription()); - assertFalse(dry.isRequired()); - assertFalse(dry.hasArg()); - - assertTrue(options.hasOption("start")); - Option start = options.getOption("start"); - assertEquals("start the database", start.getDescription()); - assertFalse(start.isRequired()); - assertFalse(start.hasArg()); - - assertTrue(options.hasOption("stop")); - Option stop = options.getOption("stop"); - assertEquals("stop the database", stop.getDescription()); - assertFalse(stop.isRequired()); - assertFalse(stop.hasArg()); - - assertTrue(options.hasOption("quiet")); - Option quiet = options.getOption("quiet"); - assertEquals("q", quiet.getOpt()); - assertEquals("don't produce any output", quiet.getDescription()); - assertFalse(quiet.isRequired()); - assertFalse(quiet.hasArg()); - - OptionGroup startStop = options.getOptionGroup(start); - assertTrue(startStop.isRequired()); - @SuppressWarnings("unchecked") - Collection<Option> groupOpts = startStop.getOptions(); - assertEquals(2, groupOpts.size()); - assertTrue(groupOpts.contains(start)); - assertTrue(groupOpts.contains(stop)); - } -}
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/ThermostatServiceTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +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.agent.cli; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.apache.commons.cli.Options; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.redhat.thermostat.agent.cli.ServiceCommand; - -public class ThermostatServiceTest { - - private ServiceCommand thermostatService; - - @Before - public void setUp() { - thermostatService = new ServiceCommand(); - } - - @After - public void tearDown() { - thermostatService = null; - } - - @Test - public void testName() { - String name = thermostatService.getName(); - assertEquals("service", name); - } - - @Test - public void testDescAndUsage() { - assertNotNull(thermostatService.getDescription()); - assertNotNull(thermostatService.getUsage()); - } - - @Test - public void testOptions() { - Options options = thermostatService.getOptions(); - assertNotNull(options); - assertEquals(0, options.getOptions().size()); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ActivatorTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,66 @@ +/* + * 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.agent.cli.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.redhat.thermostat.common.cli.Command; +import com.redhat.thermostat.test.StubBundleContext; + +public class ActivatorTest { + + @Test + public void verifyActivatorRegistersCommands() throws Exception { + + StubBundleContext bundleContext = new StubBundleContext(); + + Activator activator = new Activator(); + + activator.start(bundleContext); + + assertTrue(bundleContext.isServiceRegistered(Command.class.getName(), AgentApplication.class)); + assertTrue(bundleContext.isServiceRegistered(Command.class.getName(), ServiceCommand.class)); + assertTrue(bundleContext.isServiceRegistered(Command.class.getName(), StorageCommand.class)); + + activator.stop(bundleContext); + + assertEquals(0, bundleContext.getAllServices().size()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/AgentApplicationTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,150 @@ +/* + * 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.agent.cli.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.agent.cli.impl.AgentApplication; +import com.redhat.thermostat.agent.cli.impl.AgentApplication.ConfigurationCreator; +import com.redhat.thermostat.agent.cli.impl.AgentApplication.DAOFactoryCreator; +import com.redhat.thermostat.agent.config.AgentStartupConfiguration; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.config.InvalidConfigurationException; +import com.redhat.thermostat.common.dao.DAOFactory; +import com.redhat.thermostat.storage.core.Connection; +import com.redhat.thermostat.storage.core.Connection.ConnectionListener; +import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; +import com.redhat.thermostat.test.StubBundleContext; + +public class AgentApplicationTest { + + // TODO: Test i18nized versions when they come. + + private StubBundleContext context; + private DAOFactory daoFactory; + private Connection connection; + + private AgentApplication agent; + + private ArgumentCaptor<ConnectionListener> listenerCaptor; + + @Before + public void setUp() throws InvalidConfigurationException { + + context = new StubBundleContext(); + + AgentStartupConfiguration config = mock(AgentStartupConfiguration.class); + when(config.getDBConnectionString()).thenReturn("test string; please ignore"); + + ConfigurationCreator configCreator = mock(ConfigurationCreator.class); + when(configCreator.create()).thenReturn(config); + + listenerCaptor = ArgumentCaptor.forClass(ConnectionListener.class); + + connection = mock(Connection.class); + doNothing().when(connection).addListener(listenerCaptor.capture()); + + daoFactory = mock(DAOFactory.class); + when(daoFactory.getConnection()).thenReturn(connection); + + DAOFactoryCreator daoCreator = mock(DAOFactoryCreator.class); + when(daoCreator.create(config)).thenReturn(daoFactory); + + agent = new AgentApplication(context, configCreator, daoCreator); + } + + @After + public void tearDown() { + agent = null; + } + + @Test + public void testName() { + String name = agent.getName(); + assertEquals("agent", name); + } + + @Test + public void testDescAndUsage() { + assertNotNull(agent.getDescription()); + assertNotNull(agent.getUsage()); + } + + @Test + public void testAgentStartupRegistersDAOs() throws CommandException { + + doThrow(new ThatsAllThatWeCareAbout()).when(connection).connect(); + + Arguments args = mock(Arguments.class); + CommandContext commandContext = mock(CommandContext.class); + + when(commandContext.getArguments()).thenReturn(args); + + try { + agent.run(commandContext); + fail("not supposed to get here"); + } catch (ThatsAllThatWeCareAbout done) { + // agent.run() is so convoluted that we just want to test a part of + // it + } + + ConnectionListener listener = listenerCaptor.getValue(); + listener.changed(ConnectionStatus.CONNECTED); + + verify(daoFactory).registerDAOsAndStorageAsOSGiServices(); + } + + // FIXME test the rest of AgentApplication + + private static class ThatsAllThatWeCareAbout extends RuntimeException { + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ServiceCommandTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,81 @@ +/* + * 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.agent.cli.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.apache.commons.cli.Options; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.agent.cli.impl.ServiceCommand; + +public class ServiceCommandTest { + + private ServiceCommand thermostatService; + + @Before + public void setUp() { + thermostatService = new ServiceCommand(); + } + + @After + public void tearDown() { + thermostatService = null; + } + + @Test + public void testName() { + String name = thermostatService.getName(); + assertEquals("service", name); + } + + @Test + public void testDescAndUsage() { + assertNotNull(thermostatService.getDescription()); + assertNotNull(thermostatService.getUsage()); + } + + @Test + public void testOptions() { + Options options = thermostatService.getOptions(); + assertNotNull(options); + assertEquals(0, options.getOptions().size()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/StorageCommandTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,296 @@ +/* + * 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.agent.cli.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Properties; +import java.util.Random; +import java.util.concurrent.CountDownLatch; + +import junit.framework.Assert; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionGroup; +import org.apache.commons.cli.Options; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import com.redhat.thermostat.agent.cli.db.DBConfig; +import com.redhat.thermostat.agent.cli.db.DBStartupConfiguration; +import com.redhat.thermostat.agent.cli.db.MongoProcessRunner; +import com.redhat.thermostat.agent.cli.impl.StorageCommand; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.SimpleArguments; +import com.redhat.thermostat.common.config.InvalidConfigurationException; +import com.redhat.thermostat.common.tools.ApplicationException; +import com.redhat.thermostat.common.tools.ApplicationState; + +public class StorageCommandTest { + + private static final String PORT = "27518"; + private static final String BIND = "127.0.0.1"; + private static final String PROTOCOL = "mongodb"; + private static final String DB = "storage/db"; + + private String tmpDir; + + @Before + public void setup() { + // need to create a dummy config file for the test + try { + Random random = new Random(); + + tmpDir = System.getProperty("java.io.tmpdir") + File.separatorChar + + Math.abs(random.nextInt()) + File.separatorChar; + + System.setProperty("THERMOSTAT_HOME", tmpDir); + File base = new File(tmpDir + "storage"); + base.mkdirs(); + + File tmpConfigs = new File(base, "db.properties"); + + new File(base, "run").mkdirs(); + new File(base, "logs").mkdirs(); + new File(base, "db").mkdirs(); + + Properties props = new Properties(); + + props.setProperty(DBConfig.BIND.name(), BIND); + props.setProperty(DBConfig.PORT.name(), PORT); + props.setProperty(DBConfig.PROTOCOL.name(), PROTOCOL); + + props.store(new FileOutputStream(tmpConfigs), "thermostat test properties"); + + } catch (IOException e) { + Assert.fail("cannot setup tests: " + e); + } + } + + @Test + public void testConfig() throws CommandException { + SimpleArguments args = new SimpleArguments(); + args.addArgument("quiet", null); + args.addArgument("start", null); + args.addArgument("dryRun", null); + CommandContext ctx = mock(CommandContext.class); + when(ctx.getArguments()).thenReturn(args); + + StorageCommand service = new StorageCommand() { + @Override + MongoProcessRunner createRunner() { + throw new AssertionError("dry run should never create an actual runner"); + } + }; + + service.run(ctx); + + DBStartupConfiguration conf = service.getConfiguration(); + + Assert.assertEquals(tmpDir + DB, conf.getDBPath().getPath()); + Assert.assertEquals(Integer.parseInt(PORT), conf.getPort()); + Assert.assertEquals(PROTOCOL, conf.getProtocol()); + } + + private StorageCommand prepareService(boolean startSuccess) throws IOException, + InterruptedException, InvalidConfigurationException, ApplicationException + { + final MongoProcessRunner runner = mock(MongoProcessRunner.class); + if (!startSuccess) { + doThrow(new ApplicationException("mock exception")).when(runner).startService(); + } + + // TODO: stop not tested yet, but be sure it's not called from the code + doThrow(new ApplicationException("mock exception")).when(runner).stopService(); + + StorageCommand service = new StorageCommand() { + @Override + MongoProcessRunner createRunner() { + return runner; + } + }; + + return service; + } + + @Test + public void testListeners() throws InterruptedException, IOException, ApplicationException, InvalidConfigurationException, CommandException + { + StorageCommand service = prepareService(true); + + final CountDownLatch latch = new CountDownLatch(2); + + final boolean[] result = new boolean[2]; + service.getNotifier().addActionListener(new ActionListener<ApplicationState>() { + @Override + public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { + switch (actionEvent.getActionId()) { + case FAIL: + result[0] = false; + latch.countDown(); + latch.countDown(); + break; + + case SUCCESS: + result[0] = true; + latch.countDown(); + break; + + case START: + result[1] = true; + latch.countDown(); + break; + } + } + }); + + service.run(prepareContext()); + latch.await(); + + Assert.assertTrue(result[0]); + Assert.assertTrue(result[1]); + } + + @Test + public void testListenersFail() throws InterruptedException, IOException, ApplicationException, CommandException, InvalidConfigurationException + { + StorageCommand service = prepareService(false); + + final CountDownLatch latch = new CountDownLatch(1); + final boolean[] result = new boolean[1]; + service.getNotifier().addActionListener(new ActionListener<ApplicationState>() { + @Override + public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { + switch (actionEvent.getActionId()) { + case FAIL: + result[0] = true; + break; + + case SUCCESS: + result[0] = false; + break; + } + latch.countDown(); + } + }); + + service.run(prepareContext()); + latch.await(); + + Assert.assertTrue(result[0]); + } + + private CommandContext prepareContext() { + SimpleArguments args = new SimpleArguments(); + args.addArgument("quiet", "--quiet"); + args.addArgument("start", "--start"); + CommandContext ctx = mock(CommandContext.class); + when(ctx.getArguments()).thenReturn(args); + return ctx; + } + + @Test + public void testName() { + StorageCommand dbService = new StorageCommand(); + String name = dbService.getName(); + assertEquals("storage", name); + } + + @Test + public void testDescAndUsage() { + StorageCommand dbService = new StorageCommand(); + assertNotNull(dbService.getDescription()); + assertNotNull(dbService.getUsage()); + } + + @Ignore + @Test + public void testOptions() { + StorageCommand dbService = new StorageCommand(); + Options options = dbService.getOptions(); + assertNotNull(options); + assertEquals(4, options.getOptions().size()); + + assertTrue(options.hasOption("dryRun")); + Option dry = options.getOption("dryRun"); + assertEquals("d", dry.getOpt()); + assertEquals("run the service in dry run mode", dry.getDescription()); + assertFalse(dry.isRequired()); + assertFalse(dry.hasArg()); + + assertTrue(options.hasOption("start")); + Option start = options.getOption("start"); + assertEquals("start the database", start.getDescription()); + assertFalse(start.isRequired()); + assertFalse(start.hasArg()); + + assertTrue(options.hasOption("stop")); + Option stop = options.getOption("stop"); + assertEquals("stop the database", stop.getDescription()); + assertFalse(stop.isRequired()); + assertFalse(stop.hasArg()); + + assertTrue(options.hasOption("quiet")); + Option quiet = options.getOption("quiet"); + assertEquals("q", quiet.getOpt()); + assertEquals("don't produce any output", quiet.getDescription()); + assertFalse(quiet.isRequired()); + assertFalse(quiet.hasArg()); + + OptionGroup startStop = options.getOptionGroup(start); + assertTrue(startStop.isRequired()); + @SuppressWarnings("unchecked") + Collection<Option> groupOpts = startStop.getOptions(); + assertEquals(2, groupOpts.size()); + assertTrue(groupOpts.contains(start)); + assertTrue(groupOpts.contains(stop)); + } +}
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/PingReceiver.java Mon Dec 10 14:30:36 2012 +0100 +++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/PingReceiver.java Mon Dec 10 15:05:37 2012 +0100 @@ -45,7 +45,7 @@ @Override public Response receive(Request request) { - return new Response(ResponseType.PONG); + return new Response(ResponseType.OK); } }
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ServerHandler.java Mon Dec 10 14:30:36 2012 +0100 +++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ServerHandler.java Mon Dec 10 15:05:37 2012 +0100 @@ -125,14 +125,4 @@ e.getChannel().close(); } - private Response doRequest(Request request) { - Response response = new Response(ResponseType.ERROR); - switch ((RequestType) request.getType()) { - case RESPONSE_EXPECTED: - response = new Response(ResponseType.PONG); - break; - default: - } - return response; - } }
--- a/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/ResponseEncoderTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/ResponseEncoderTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -68,7 +68,7 @@ @Before public void setUp() { - Response r = new Response(ResponseType.PONG); + Response r = new Response(ResponseType.OK); e = mock(MessageEvent.class); when(e.getMessage()).thenReturn(r); when(e.getFuture()).thenReturn(null); @@ -89,10 +89,10 @@ ChannelBuffer buf = (ChannelBuffer) argument.getValue(); int messageLength = buf.readInt(); - assertEquals(4, messageLength); + assertEquals(2, messageLength); ByteBuffer bbuf = ByteBuffer.allocate(buf.readableBytes()); buf.readBytes(bbuf); String message = new String(bbuf.array()); - assertEquals("PONG", message); + assertEquals("OK", message); } }
--- a/agent/heapdumper/src/main/java/com/redhat/thermostat/agent/heapdumper/internal/HeapDumpReceiver.java Mon Dec 10 14:30:36 2012 +0100 +++ b/agent/heapdumper/src/main/java/com/redhat/thermostat/agent/heapdumper/internal/HeapDumpReceiver.java Mon Dec 10 15:05:37 2012 +0100 @@ -84,10 +84,10 @@ } catch (IOException e) { log.log(Level.SEVERE, "Unexpected IO problem while writing heap dump", e); - return new Response(ResponseType.EXCEPTION); + return new Response(ResponseType.ERROR); } catch (HeapDumpException e) { log.log(Level.SEVERE, "Unexpected problem while writing heap dump", e); - return new Response(ResponseType.EXCEPTION); + return new Response(ResponseType.ERROR); } return new Response(ResponseType.OK); }
--- a/agent/heapdumper/src/test/java/com/redhat/thermostat/agent/heapdumper/internal/HeapDumpReceiverTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/agent/heapdumper/src/test/java/com/redhat/thermostat/agent/heapdumper/internal/HeapDumpReceiverTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -127,7 +127,7 @@ doThrow(new HeapDumpException()).when(jmapDumper).dumpHeap(anyString(), anyString()); Response response = receiver.receive(request); - assertEquals(ResponseType.EXCEPTION, response.getType()); + assertEquals(ResponseType.ERROR, response.getType()); } @@ -137,7 +137,7 @@ Response response = receiver.receive(request); - assertEquals(ResponseType.EXCEPTION, response.getType()); + assertEquals(ResponseType.ERROR, response.getType()); } }
--- a/client/command/src/main/java/com/redhat/thermostat/client/command/cli/PingCommand.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/command/src/main/java/com/redhat/thermostat/client/command/cli/PingCommand.java Mon Dec 10 15:05:37 2012 +0100 @@ -79,14 +79,10 @@ case ERROR: out.println(translator.localize(LocaleResources.COMMAND_PING_RESPONSE_ERROR, request.getTarget().toString())); break; - case PONG: case OK: case NOOP: out.println(translator.localize(LocaleResources.COMMAND_PING_RESPONSE_OK, request.getTarget().toString())); break; - case EXCEPTION: - out.println(translator.localize(LocaleResources.COMMAND_PING_RESPONSE_EXCEPTION)); - break; case NOK: out.println(translator.localize(LocaleResources.COMMAND_PING_RESPONSE_REFUSED)); break;
--- a/client/command/src/main/java/com/redhat/thermostat/client/command/internal/LocaleResources.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/command/src/main/java/com/redhat/thermostat/client/command/internal/LocaleResources.java Mon Dec 10 15:05:37 2012 +0100 @@ -49,7 +49,6 @@ COMMAND_PING_NO_AGENT_INFO_DAO, COMMAND_PING_RESPONSE_ERROR, - COMMAND_PING_RESPONSE_EXCEPTION, COMMAND_PING_RESPONSE_OK, COMMAND_PING_RESPONSE_REFUSED, COMMAND_PING_RESPONSE_UNKNOWN,
--- a/client/command/src/main/java/com/redhat/thermostat/client/command/internal/ResponseHandler.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/command/src/main/java/com/redhat/thermostat/client/command/internal/ResponseHandler.java Mon Dec 10 15:05:37 2012 +0100 @@ -72,7 +72,7 @@ @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { // TODO when response has support for parameters, provide the exception as well. - Response response = new Response(ResponseType.EXCEPTION); + Response response = new Response(ResponseType.ERROR); notifyListeners(response); }
--- a/client/command/src/main/resources/com/redhat/thermostat/client/command/internal/strings.properties Mon Dec 10 14:30:36 2012 +0100 +++ b/client/command/src/main/resources/com/redhat/thermostat/client/command/internal/strings.properties Mon Dec 10 15:05:37 2012 +0100 @@ -6,7 +6,6 @@ COMMAND_PING_NO_AGENT_INFO_DAO = Unable to access agent information: service not available COMMAND_PING_RESPONSE_ERROR = Error received from: {0} -COMMAND_PING_RESPONSE_EXCEPTION = The tubes, they are probably broken. COMMAND_PING_RESPONSE_OK = Response received from: {0} COMMAND_PING_RESPONSE_REFUSED = The server refused to PONG our PING? COMMAND_PING_RESPONSE_UNKNOWN = Unknown result from ping command \ No newline at end of file
--- a/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/HeapDumpController.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/HeapDumpController.java Mon Dec 10 15:05:37 2012 +0100 @@ -163,6 +163,14 @@ public void run() { view.notifyHeapDumpComplete(); } + }, new Runnable() { + @Override + public void run() { + view.displayWarning(translator.localize( + LocaleResources.HEAP_DUMP_ERROR, + ref.getAgent().getAgentId(), + ref.getIdString())); + } }); break;
--- a/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/HeapView.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/HeapView.java Mon Dec 10 15:05:37 2012 +0100 @@ -78,4 +78,6 @@ public abstract void updateHeapDumpList(List<HeapDump> heapDumps); + public abstract void displayWarning(String string); + }
--- a/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/LocaleResources.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/LocaleResources.java Mon Dec 10 15:05:37 2012 +0100 @@ -57,6 +57,7 @@ HEAP_ID_NOT_FOUND, HEAP_ID_REQUIRED, SEARCH_TERM_REQUIRED, + HEAP_DUMP_ERROR, COMMAND_HEAP_DUMP_DONE,
--- a/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/cli/DumpHeapCommand.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/cli/DumpHeapCommand.java Mon Dec 10 15:05:37 2012 +0100 @@ -71,28 +71,33 @@ } @Override - public void run(CommandContext ctx) throws CommandException { + public void run(final CommandContext ctx) throws CommandException { HostVMArguments args = new HostVMArguments(ctx.getArguments()); final Semaphore s = new Semaphore(0); - Runnable r = new Runnable() { - + Runnable successHandler = new Runnable() { @Override public void run() { + ctx.getConsole().getOutput().println(translator.localize(LocaleResources.COMMAND_HEAP_DUMP_DONE)); s.release(); } }; + Runnable errorHandler = new Runnable() { + public void run() { + ctx.getConsole().getError().println(translator.localize(LocaleResources.HEAP_DUMP_ERROR)); + s.release(); + } + }; + AgentInfoDAO service = serviceProvider.getService(AgentInfoDAO.class); if (service == null) { throw new CommandException("Unable to access agent information"); } - implementation.execute(service, args.getVM(), r); + implementation.execute(service, args.getVM(), successHandler, errorHandler); serviceProvider.ungetService(AgentInfoDAO.class, service); try { s.acquire(); - ctx.getConsole().getOutput().print(translator.localize(LocaleResources.COMMAND_HEAP_DUMP_DONE)); - ctx.getConsole().getOutput().print("\n"); } catch (InterruptedException ex) { // Nothing to do here, just return ASAP. }
--- a/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/cli/HeapDumperCommand.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/main/java/com/redhat/thermostat/client/heap/cli/HeapDumperCommand.java Mon Dec 10 15:05:37 2012 +0100 @@ -43,6 +43,7 @@ import com.redhat.thermostat.common.command.Request.RequestType; import com.redhat.thermostat.common.command.RequestResponseListener; import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; import com.redhat.thermostat.common.dao.AgentInfoDAO; import com.redhat.thermostat.common.dao.HostRef; import com.redhat.thermostat.common.dao.VmRef; @@ -55,20 +56,30 @@ private class HeapDumpListener implements RequestResponseListener { - private Runnable heapDumpCompleteAction; + private Runnable successAction; + private Runnable failureAction; - private HeapDumpListener(Runnable heapDumpCompleteAction) { - this.heapDumpCompleteAction = heapDumpCompleteAction; + private HeapDumpListener(Runnable heapDumpSuccessfulAction, Runnable heapDumpFailureAction) { + this.successAction = heapDumpSuccessfulAction; + this.failureAction = heapDumpFailureAction; } @Override public void fireComplete(Request request, Response response) { - heapDumpCompleteAction.run(); + if (response.getType() == ResponseType.ERROR) { + if (failureAction != null) { + failureAction.run(); + } + } else { + if (successAction != null) { + successAction.run(); + } + } } - + } - public void execute(AgentInfoDAO agentInfoDAO, VmRef reference, Runnable heapDumpCompleteAction) { + public void execute(AgentInfoDAO agentInfoDAO, VmRef reference, Runnable heapDumpSuccessAction, Runnable heapDumpFailureAction) { HostRef targetHostRef = reference.getAgent(); String address = agentInfoDAO.getAgentInformation(targetHostRef).getConfigListenAddress(); @@ -78,7 +89,7 @@ Request req = new Request(RequestType.RESPONSE_EXPECTED, target); req.setReceiver(RECEIVER_CLASS_NAME); req.setParameter(VM_ID_PARAM, reference.getIdString()); - req.addListener(new HeapDumpListener(heapDumpCompleteAction)); + req.addListener(new HeapDumpListener(heapDumpSuccessAction, heapDumpFailureAction)); RequestQueue queue = OSGIUtils.getInstance().getService(RequestQueue.class); queue.putRequest(req);
--- a/client/heapdumper/core/src/main/resources/com/redhat/thermostat/client/heap/strings.properties Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/main/resources/com/redhat/thermostat/client/heap/strings.properties Mon Dec 10 15:05:37 2012 +0100 @@ -15,7 +15,7 @@ HEAP_ID_NOT_FOUND = Heap ID not found: {0} HEAP_ID_REQUIRED = Heap ID required SEARCH_TERM_REQUIRED = A search term is required - +HEAP_DUMP_ERROR = Error dumping heap (agent: {0}, vm: {1}) COMMAND_HEAP_DUMP_DONE = Done
--- a/client/heapdumper/core/src/test/java/com/redhat/thermostat/client/heap/HeapDumpControllerTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/test/java/com/redhat/thermostat/client/heap/HeapDumpControllerTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -68,6 +68,7 @@ import com.redhat.thermostat.common.TimerFactory; import com.redhat.thermostat.common.dao.AgentInfoDAO; import com.redhat.thermostat.common.dao.HeapDAO; +import com.redhat.thermostat.common.dao.HostRef; import com.redhat.thermostat.common.dao.VmMemoryStatDAO; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.common.heap.HeapDump; @@ -162,7 +163,14 @@ ApplicationCache cache = mock(ApplicationCache.class); when(appService.getApplicationCache()).thenReturn(cache); setUpTimers(); + + HostRef hostRef = mock(HostRef.class); + when(hostRef.getAgentId()).thenReturn("agent-id"); + VmRef ref = mock(VmRef.class); + when(ref.getIdString()).thenReturn("vm-id"); + when(ref.getAgent()).thenReturn(hostRef); + heapDumperCommand = mock(HeapDumperCommand.class); controller = new HeapDumpController(agentDao, vmDao, heapDao, ref, appService, heapDumperCommand, viewProvider, detailsViewProvider, @@ -285,12 +293,23 @@ heapDumperListener.actionPerformed(new ActionEvent<HeapDumperAction>(view, HeapDumperAction.DUMP_REQUESTED)); ArgumentCaptor<Runnable> heapDumpCompleteAction = ArgumentCaptor.forClass(Runnable.class); - verify(heapDumperCommand).execute(same(agentDao), any(VmRef.class), heapDumpCompleteAction.capture()); + verify(heapDumperCommand).execute(same(agentDao), any(VmRef.class), heapDumpCompleteAction.capture(), any(Runnable.class)); heapDumpCompleteAction.getValue().run(); verify(view).notifyHeapDumpComplete(); - } - + + @Test + public void testRequestHeapDumpFails() { + setUpListeners(); + + heapDumperListener.actionPerformed(new ActionEvent<HeapDumperAction>(view, HeapDumperAction.DUMP_REQUESTED)); + + ArgumentCaptor<Runnable> heapDumpFailedAction = ArgumentCaptor.forClass(Runnable.class); + verify(heapDumperCommand).execute(same(agentDao), any(VmRef.class), any(Runnable.class), heapDumpFailedAction.capture()); + heapDumpFailedAction.getValue().run(); + verify(view).displayWarning("Error dumping heap (agent: agent-id, vm: vm-id)"); + } + @SuppressWarnings("unchecked") @Test public void testTimerChecksForNewHeapDumps() {
--- a/client/heapdumper/core/src/test/java/com/redhat/thermostat/client/heap/cli/DumpHeapCommandTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/test/java/com/redhat/thermostat/client/heap/cli/DumpHeapCommandTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -77,15 +77,15 @@ when(osgi.getService(AgentInfoDAO.class)).thenReturn(agentInfoDao); HeapDumperCommand impl = mock(HeapDumperCommand.class); - final ArgumentCaptor<Runnable> arg = ArgumentCaptor.forClass(Runnable.class); + final ArgumentCaptor<Runnable> successHandler = ArgumentCaptor.forClass(Runnable.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { - arg.getValue().run(); + successHandler.getValue().run(); return null; } - }).when(impl).execute(eq(agentInfoDao), any(VmRef.class), arg.capture()); + }).when(impl).execute(eq(agentInfoDao), any(VmRef.class), successHandler.capture(), any(Runnable.class)); DumpHeapCommand command = new DumpHeapCommand(osgi, impl); @@ -97,7 +97,7 @@ command.run(factory.createContext(args)); - verify(impl).execute(eq(agentInfoDao), isA(VmRef.class), any(Runnable.class)); + verify(impl).execute(eq(agentInfoDao), isA(VmRef.class), any(Runnable.class), any(Runnable.class)); assertEquals("Done\n", factory.getOutput()); }
--- a/client/heapdumper/core/src/test/java/com/redhat/thermostat/client/heap/cli/HeapDumperCommandTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/core/src/test/java/com/redhat/thermostat/client/heap/cli/HeapDumperCommandTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -38,6 +38,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -70,6 +71,7 @@ private VmRef vmRef; private RequestQueue reqQueue; private Runnable heapDumpCompleteAction; + private Runnable heapDumpFailedAction; @Before public void setUp() { @@ -91,7 +93,7 @@ when(vmRef.getIdString()).thenReturn("123"); when(vmRef.getAgent()).thenReturn(host); heapDumpCompleteAction = mock(Runnable.class); - + heapDumpFailedAction = mock(Runnable.class); } @After @@ -105,7 +107,7 @@ @Test public void testExecute() { - cmd.execute(agentInfoDao, vmRef, heapDumpCompleteAction); + cmd.execute(agentInfoDao, vmRef, heapDumpCompleteAction, heapDumpFailedAction); ArgumentCaptor<Request> reqArg = ArgumentCaptor.forClass(Request.class); verify(reqQueue).putRequest(reqArg.capture()); @@ -121,6 +123,29 @@ l.fireComplete(req, new Response(ResponseType.OK)); } verify(heapDumpCompleteAction).run(); + verify(heapDumpFailedAction, times(0)).run(); + } + + @Test + public void testExecuteFailure() { + + cmd.execute(agentInfoDao, vmRef, heapDumpCompleteAction, heapDumpFailedAction); + + ArgumentCaptor<Request> reqArg = ArgumentCaptor.forClass(Request.class); + verify(reqQueue).putRequest(reqArg.capture()); + Request req = reqArg.getValue(); + assertEquals("com.redhat.thermostat.agent.heapdumper.internal.HeapDumpReceiver", req.getReceiver()); + verifyClassExists(req.getReceiver()); + assertEquals(RequestType.RESPONSE_EXPECTED, req.getType()); + assertEquals("123", req.getParameter("vmId")); + assertEquals(new InetSocketAddress("test", 123), req.getTarget()); + + Collection<RequestResponseListener> ls = req.getListeners(); + for (RequestResponseListener l : ls) { + l.fireComplete(req, new Response(ResponseType.ERROR)); + } + verify(heapDumpCompleteAction, times(0)).run(); + verify(heapDumpFailedAction).run(); } private void verifyClassExists(String receiver) {
--- a/client/heapdumper/swing/src/main/java/com/redhat/thermostat/client/heap/swing/HeapSwingView.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/heapdumper/swing/src/main/java/com/redhat/thermostat/client/heap/swing/HeapSwingView.java Mon Dec 10 15:05:37 2012 +0100 @@ -43,6 +43,7 @@ import java.util.List; import javax.swing.BoxLayout; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; @@ -227,4 +228,9 @@ } }); } + + @Override + public void displayWarning(String string) { + JOptionPane.showMessageDialog(visiblePane, string, "Warning", JOptionPane.WARNING_MESSAGE); + } }
--- a/client/memory-stats-panel/core/pom.xml Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <artifactId>thermostat-osgi-memory-stats-panel</artifactId> - <groupId>com.redhat.thermostat</groupId> - <version>0.5.0-SNAPSHOT</version> - </parent> - <artifactId>thermostat-osgi-memory-stats-panel-core</artifactId> - <packaging>bundle</packaging> - <name>Thermostat Client VM Memory Monitor Core plugin</name> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <extensions>true</extensions> - <configuration> - <instructions> - <Export-Package> - com.redhat.thermostat.client.stats.memory.core, - com.redhat.thermostat.client.stats.memory.core.locale, - </Export-Package> - <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> - <Bundle-SymbolicName>com.redhat.thermostat.client.stats.memory.core</Bundle-SymbolicName> - <!-- Do not autogenerate uses clauses in Manifests --> - <_nouses>true</_nouses> - </instructions> - </configuration> - </plugin> - </plugins> - </build> - <dependencies> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.osgi</groupId> - <artifactId>org.osgi.core</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.osgi</groupId> - <artifactId>org.osgi.compendium</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.jfree</groupId> - <artifactId>jfreechart</artifactId> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-common-core</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-client-core</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-gc-remote-collector-client-common</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-gc-remote-collector-client-common</artifactId> - <version>${project.version}</version> - </dependency> - </dependencies> -</project>
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryMeter.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,433 +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.stats.memory.core; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.GradientPaint; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Insets; -import java.awt.Paint; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.geom.Rectangle2D; -import java.awt.geom.RoundRectangle2D; -import java.awt.image.BufferedImage; -import java.beans.Transient; - -import javax.swing.JComponent; -import javax.swing.plaf.ColorUIResource; - -import sun.swing.SwingUtilities2; - -@SuppressWarnings({ "restriction", "serial" }) -public class MemoryMeter extends JComponent { - - // TODO the font should be customizable - private static final Font font = new Font("SansSerif", Font.PLAIN, 10); - - private static final int TICK_NUM = 100; - private static final int SMALL_TICK_NUM = 5; - - private static final int MAIN_BAR_HEIGHT = 20; - - private static final ColorUIResource MAIN_GRADIENT_TOP = new ColorUIResource(0xf1f3f1); - private static final ColorUIResource MAIN_BORDER_COLOR = new ColorUIResource(0xa8aca8); - - private static final ColorUIResource MAIN_BAR_BASE_COLOR_TOP = new ColorUIResource(0xbcd5ef); - private static final ColorUIResource MAIN_BAR_BASE_COLOR = new ColorUIResource(0x4A90D9); - - //private static final ColorUIResource STATS_BG = new ColorUIResource(0xF8F8F8); - private static final ColorUIResource STATS_BG = new ColorUIResource(0xFFFFFF); - - private ColorUIResource tickColor; - - private RangeModel primary; - private RangeModel secondary; - - private Insets boundInsets; - - private RangeModel internalSecondaryModel; - - private StatsModel primaryStats; - - private String primaryUnit; - private String secondaryUnit; - - public void setPrimaryScaleUnit(String primaryUnit) { - this.primaryUnit = primaryUnit; - } - - public void setSecondayScaleUnit(String secondaryUnit) { - this.secondaryUnit = secondaryUnit; - } - - public StatsModel getStats() { - return primaryStats; - } - - public void setStats(StatsModel primaryStats) { - this.primaryStats = primaryStats; - } - - public MemoryMeter() { - - secondaryUnit = ""; - primaryUnit = ""; - - boundInsets = new Insets(10, 10, 20, 20); - - tickColor = new ColorUIResource(0xdbdddb); - - primary = new RangeModel(); - primary.setMinimum(0); - primary.setMaximum(100); - primary.setMinNormalized(0); - primary.setMaxNormalized(100); - - secondary = new RangeModel(); - secondary.setMinimum(0); - secondary.setMaximum(100); - secondary.setMinNormalized(0); - secondary.setMaxNormalized(100); - - internalSecondaryModel = new RangeModel(); - } - - public ColorUIResource getTickColor() { - return tickColor; - } - - public void setTickColor(ColorUIResource tickColor) { - this.tickColor = tickColor; - } - - public RangeModel getPrimaryModel() { - return primary; - } - - public RangeModel getSecondaryModel() { - return secondary; - } - - protected Rectangle getOuterBounds() { - return new Rectangle(0, 0, getWidth(), getHeight()); - } - - protected Rectangle getBoundsWithInsets(Rectangle bounds) { - return new Rectangle(bounds.x + boundInsets.left, - bounds.y + boundInsets.top, - bounds.width - boundInsets.right, - bounds.height - boundInsets.bottom); - } - - /** - * paint the outher frame, including the light border sorrounding - */ - protected void paintOuterFrame(Graphics2D graphics, Rectangle bounds) { - - RoundRectangle2D frame = new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height, 6, 6); - - Paint paint = new GradientPaint(0, 0, MAIN_GRADIENT_TOP, 0, getHeight(), getBackground()); - graphics.setPaint(paint); - graphics.fill(frame); - - paint = new GradientPaint(0, 0, MAIN_BORDER_COLOR, 0, getHeight(), getBackground()); - graphics.setPaint(paint); - frame = new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width -1, bounds.height, 6, 6); - graphics.draw(frame); - } - - /** - * paint the track sorrounding the main bar - */ - protected void paintMainBarTrackFill(Graphics2D graphics, Rectangle bounds) { - - Paint paint = new GradientPaint(0, 0, MAIN_GRADIENT_TOP, 0, bounds.height, Color.WHITE); - graphics.setPaint(paint); - RoundRectangle2D frame = new RoundRectangle2D.Float(0, 0, bounds.width, bounds.height, 6, 6); - graphics.fill(frame); - } - - /** - */ - protected void paintMainBarTrackBorder(Graphics2D graphics, Rectangle bounds) { - Paint paint = new GradientPaint(0, 0, MAIN_BORDER_COLOR, getWidth(), 0, getBackground()); - graphics.setPaint(paint); - - RoundRectangle2D frame = new RoundRectangle2D.Float(0, 0, bounds.width - 1, bounds.height, 6, 6); - graphics.draw(frame); - } - - - /** - * this is the main bar, will it up to what is defined by the model - */ - protected void paintMainBarFill(Graphics2D graphics, Rectangle bounds) { - Paint paint = new GradientPaint(0, 0, MAIN_BAR_BASE_COLOR, getWidth() * 2, 0, getBackground()); - graphics.setPaint(paint); - - RoundRectangle2D frame = - new RoundRectangle2D.Float(0, 0, getPrimaryModel().getValueNormalized(), - bounds.height, 6, 6); - graphics.fill(frame); - - String value = String.valueOf(getPrimaryModel().getValue()) + " " + primaryUnit; - Rectangle2D fontBounds = font.getStringBounds(value, graphics.getFontRenderContext()); - int width = (int) (bounds.width/2 - fontBounds.getWidth()/2) - 1; - - if (width > getPrimaryModel().getValueNormalized()) { - graphics.setPaint(MAIN_BAR_BASE_COLOR); - } else { - graphics.setPaint(getBackground()); - } - - int height = (int) (bounds.height / 2 + fontBounds.getHeight()/2); - SwingUtilities2.drawString(this, graphics, value, width, height); - } - - /** - */ - private void paintMainBar(Graphics2D g, Rectangle bounds) { - - Graphics2D graphics = (Graphics2D) g.create(); - - graphics.translate(bounds.x, bounds.y); - paintMainBarTrackFill(graphics, bounds); - - paintMainBarFill(graphics, bounds); - - paintMainBarTrackBorder(graphics, bounds); - graphics.dispose(); - } - - /** - */ - protected void drawBottomBar(Graphics2D g, Rectangle bounds) { - - Graphics2D graphics = (Graphics2D) g.create(); - graphics.translate(bounds.x, bounds.y + bounds.height); - - drawTickMark(graphics, bounds); - - paintSecondaryBarFill(graphics, bounds); - - graphics.dispose(); - } - - /** - */ - protected void drawTickMark(Graphics2D graphics, Rectangle bounds) { - - graphics.setPaint(MAIN_BORDER_COLOR); - - int smallTop = bounds.height - 5; - int smallBottom = bounds.height; - - int mainTop = smallTop + 20; - int mainBottom = smallBottom - 15; - - // the first and last tick are always big, this is the first - graphics.drawLine(0, mainTop, 0, mainBottom); - - // the space between the vertical lines - double tickSpace = ((double) bounds.width) / TICK_NUM; - - internalSecondaryModel.setMaxNormalized(bounds.width); - int numTicks = 0; - - for (int x = 0; x < bounds.width; x += tickSpace + 0.5) { - if (numTicks % SMALL_TICK_NUM == 0) { - - graphics.drawLine(x, smallTop, x, smallBottom + 5); - - internalSecondaryModel.setValue(x); - } else { - graphics.drawLine(x, smallTop, x, smallBottom); - } - numTicks++; - } - - // that's the last - graphics.drawLine(bounds.width, mainTop, bounds.width, mainBottom); - graphics.drawLine(0, smallTop, bounds.width, smallTop); - - drawStrings(graphics, mainBottom, mainTop, bounds.width); - } - - protected void drawStrings(Graphics2D graphics, int top, int bottom, int right) { - - // now draw the min/max values of both side of markers - // top bar min value - FontMetrics fm = SwingUtilities2.getFontMetrics(this, font); - - String value = String.valueOf(getPrimaryModel().getMinimum()) + " " + primaryUnit; - int height = top + fm.getAscent()/2; - SwingUtilities2.drawString(this, graphics, value, 1, height); - - value = String.valueOf(getSecondaryModel().getMinimum()) + " " + secondaryUnit; - height = bottom; - SwingUtilities2.drawString(this, graphics, value, 1, height); - - value = String.valueOf(getPrimaryModel().getMaximum()) + " " + primaryUnit; - height = top + fm.getAscent()/2; - - int width = (int) (right - font.getStringBounds(value, graphics.getFontRenderContext()).getWidth()) - 1; - SwingUtilities2.drawString(this, graphics, value, width, height); - - value = String.valueOf(getSecondaryModel().getMaximum()) + " " + secondaryUnit; - height = bottom; - width = (int) (right - font.getStringBounds(value, graphics.getFontRenderContext()).getWidth()) - 1; - SwingUtilities2.drawString(this, graphics, value, width, height); - - // now draw the actual value for the bottom bar, the top bar is drawn in - // its fill method - value = String.valueOf(getSecondaryModel().getValue()) + " " + secondaryUnit; - width = right/2; - Rectangle2D bounds = font.getStringBounds(value, graphics.getFontRenderContext()); - width = (int) (width - bounds.getWidth()/2) - 1; - SwingUtilities2.drawString(this, graphics, value, width, height); - RoundRectangle2D frame = new RoundRectangle2D.Double(width - 2, height - bounds.getHeight(), - bounds.getWidth() + 4, bounds.getHeight() + 4, - 4, 4); - graphics.draw(frame); - } - - protected void paintSecondaryBarFill(Graphics2D graphics, Rectangle bounds) { - - graphics.setPaint(MAIN_BAR_BASE_COLOR_TOP); - graphics.drawLine(1, bounds.height, getSecondaryModel().getValueNormalized() - 1, bounds.height); - - graphics.setPaint(MAIN_BAR_BASE_COLOR); - graphics.fillRect(1, bounds.height + 1, getSecondaryModel().getValueNormalized(), 2); - } - - protected void paintStats(Graphics2D graphics, Rectangle bounds) { - - int imageWidth = bounds.width - 2; - if (imageWidth < 0 || bounds.height < 0) { - return; - } - - StatsModel stats = getStats(); - drawStats(graphics, stats, bounds.x, bounds.y, imageWidth, bounds.height); - } - - private void drawStats(Graphics2D graphics, StatsModel stats, int x, int y, int imageWidth, int height) { - if (stats != null) { - BufferedImage image = stats.getChart(imageWidth, height, STATS_BG, - new ColorUIResource(getForeground())); - - paintStatsLabel(graphics, image, stats.getName()); - graphics.drawImage(image, x, y, null); - } - } - - protected void paintStatsLabel(Graphics2D graphics, BufferedImage image, String label) { - int height = SwingUtilities2.getFontMetrics(this, font).getAscent() + 2; - if (height <= 0) { - return; - } - - Graphics2D imageGraphics = (Graphics2D) image.getGraphics(); - imageGraphics.setColor(getForeground()); - imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - SwingUtilities2.drawString(this, imageGraphics, label, 2, height); - } - - @Override - protected void paintComponent(Graphics g) { - - Graphics2D graphics = (Graphics2D) g.create(); - graphics.setFont(font); - - graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - - Rectangle outerBounds = getOuterBounds(); - Rectangle innerBounds = getBoundsWithInsets(outerBounds); - Rectangle statsBound = getBoundsWithInsets(outerBounds); - - // move the bar close to the center - innerBounds.height = MAIN_BAR_HEIGHT; - innerBounds.y = outerBounds.height/2; - - // make the stats area cover the upper portion instead - statsBound.height = (outerBounds.height/2) - MAIN_BAR_HEIGHT; - - resetModels(0, innerBounds.width); - - // some eye candy - paintOuterFrame(graphics, outerBounds); - - // paint the usage stats - paintStats(graphics, statsBound); - - // main and bottom bars - paintMainBar(graphics, innerBounds); - drawBottomBar(graphics, innerBounds); - - graphics.dispose(); - } - - private void resetModels(int min, int max) { - - getPrimaryModel().setMaxNormalized(max); - getPrimaryModel().setMinNormalized(min); - - RangeModel model = getSecondaryModel(); - model.setMaxNormalized(max); - model.setMinNormalized(min); - - internalSecondaryModel.setMaximum(model.getMaximum()); - internalSecondaryModel.setMinimum(model.getMinimum()); - internalSecondaryModel.setValue(model.getValue()); - - internalSecondaryModel.setMaxNormalized(max); - internalSecondaryModel.setMinNormalized(0); - } - - @Override - @Transient - public Dimension getPreferredSize() { - return new Dimension(850, 150); - } -}
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsController.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,240 +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.stats.memory.core; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import com.redhat.thermostat.client.core.controllers.VmInformationServiceController; -import com.redhat.thermostat.client.core.views.BasicView.Action; -import com.redhat.thermostat.client.core.views.UIComponent; -import com.redhat.thermostat.client.stats.memory.core.locale.LocaleResources; -import com.redhat.thermostat.common.ActionEvent; -import com.redhat.thermostat.common.ActionListener; -import com.redhat.thermostat.common.ApplicationService; -import com.redhat.thermostat.common.NotImplementedException; -import com.redhat.thermostat.common.Timer; -import com.redhat.thermostat.common.Timer.SchedulingType; -import com.redhat.thermostat.common.dao.AgentInfoDAO; -import com.redhat.thermostat.common.dao.VmMemoryStatDAO; -import com.redhat.thermostat.common.dao.VmRef; -import com.redhat.thermostat.common.locale.Translate; -import com.redhat.thermostat.common.utils.DisplayableValues.Scale; -import com.redhat.thermostat.gc.remote.common.GCRequest; -import com.redhat.thermostat.gc.remote.common.command.GCCommand; -import com.redhat.thermostat.storage.model.VmMemoryStat; -import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; -import com.redhat.thermostat.storage.model.VmMemoryStat.Space; - -class MemoryStatsController implements VmInformationServiceController { - - private final MemoryStatsView view; - private final VmMemoryStatDAO vmDao; - - private final VmRef ref; - private final Timer timer; - - private final Map<String, Payload> regions; - - private VMCollector collector; - - class VMCollector implements Runnable { - - private long desiredUpdateTimeStamp = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1); - - @Override - public void run() { - List<VmMemoryStat> vmInfo = vmDao.getLatestVmMemoryStats(ref, desiredUpdateTimeStamp); - for (VmMemoryStat memoryStats: vmInfo) { - Generation[] generations = memoryStats.getGenerations(); - - for (Generation generation : generations) { - Space[] spaces = generation.getSpaces(); - for (Space space: spaces) { - Payload payload = regions.get(space.getName()); - if (payload == null) { - payload = new Payload(); - payload.setName(space.getName()); - } - - Scale usedScale = normalizeScale(space.getUsed(), space.getCapacity()); - double used = Scale.convertTo(usedScale, space.getUsed(), 100); - double maxUsed = Scale.convertTo(usedScale, space.getCapacity(), 100); - - payload.setUsed(used); - payload.setMaxUsed(maxUsed); - payload.setUsedUnit(usedScale); - - Scale maxScale = normalizeScale(space.getCapacity(), space.getMaxCapacity()); - double capacity = Scale.convertTo(maxScale, space.getCapacity(), 100); - double maxCapacity = Scale.convertTo(maxScale, space.getMaxCapacity(), 100); - - payload.setCapacity(capacity); - payload.setMaxCapacity(maxCapacity); - payload.setCapacityUnit(maxScale); - - String tooltip = space.getName() + ": used: " + used + " " + usedScale + - ", capacity: " + capacity + " " + maxScale + - ", max capacity: " + maxCapacity + " " + maxScale; - - payload.setTooltip(tooltip); - - StatsModel model = payload.getModel(); - if (model == null) { - model = new StatsModel(); - model.setName(space.getName()); - model.setRange(3600); - } - - // normalize this always in the same unit - model.addData(memoryStats.getTimeStamp(), - Scale.convertTo(Scale.MiB, space.getUsed(), 100)); - - payload.setModel(model); - if (regions.containsKey(space.getName())) { - view.updateRegion(payload.clone()); - } else { - view.addRegion(payload.clone()); - regions.put(space.getName(), payload); - } - - view.requestRepaint(); - desiredUpdateTimeStamp = Math.max(desiredUpdateTimeStamp, memoryStats.getTimeStamp()); - } - } - } - } - } - - public MemoryStatsController(ApplicationService appSvc, final VmMemoryStatDAO vmMemoryStatDao, - final VmRef ref, MemoryStatsViewProvider viewProvider, - final AgentInfoDAO agentDAO, final GCRequest gcRequest) { - - regions = new HashMap<>(); - this.ref = ref; - vmDao = vmMemoryStatDao; - view = viewProvider.createView(); - - timer = appSvc.getTimerFactory().createTimer(); - - collector = new VMCollector(); - timer.setAction(collector); - - timer.setInitialDelay(0); - timer.setDelay(1000); - timer.setTimeUnit(TimeUnit.MILLISECONDS); - timer.setSchedulingType(SchedulingType.FIXED_RATE); - - view.addActionListener(new ActionListener<Action>() { - @Override - public void actionPerformed(ActionEvent<Action> actionEvent) { - switch(actionEvent.getActionId()) { - case HIDDEN: - stop(); - break; - - case VISIBLE: - start(); - break; - - default: - throw new NotImplementedException("unknown event: " + actionEvent.getActionId()); - } - } - }); - - view.addGCActionListener(new ActionListener<GCCommand>() { - @Override - public void actionPerformed(ActionEvent<GCCommand> actionEvent) { - gcRequest.sendGCRequestToAgent(ref, agentDAO); - } - }); - } - - // for testing - VMCollector getCollector() { - return collector; - }; - - Map<String, Payload> getRegions() { - return regions; - } - - private Scale normalizeScale(long min, long max) { - // FIXME: this is very dumb and very inefficient - // needs cleanup - Scale minScale = Scale.getScale(min); - Scale maxScale = Scale.getScale(max); - - Scale[] scales = Scale.values(); - int maxID = 0; - int minID = 0; - for (int i = 0; i < scales.length; i++) { - if (scales[i] == minScale) { - minID = i; - } - if (scales[i] == maxScale) { - maxID = i; - } - } - while (maxID - minID >= 2) { - minID++; - } - return scales[minID]; - } - - private void start() { - timer.start(); - } - - private void stop() { - timer.stop(); - } - - @Override - public String getLocalizedName() { - Translate<LocaleResources> t = LocaleResources.createLocalizer(); - return t.localize(LocaleResources.VM_INFO_TAB_MEMORY); - } - - @Override - public UIComponent getView() { - return (UIComponent) view; - } -}
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsService.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +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.stats.memory.core; - -import com.redhat.thermostat.client.core.VmFilter; -import com.redhat.thermostat.client.core.VmInformationService; -import com.redhat.thermostat.client.core.controllers.VmInformationServiceController; -import com.redhat.thermostat.client.osgi.service.AlwaysMatchFilter; -import com.redhat.thermostat.common.ApplicationService; -import com.redhat.thermostat.common.dao.AgentInfoDAO; -import com.redhat.thermostat.common.dao.VmMemoryStatDAO; -import com.redhat.thermostat.common.dao.VmRef; -import com.redhat.thermostat.common.utils.OSGIUtils; -import com.redhat.thermostat.gc.remote.common.GCRequest; - -public class MemoryStatsService implements VmInformationService { - - private static final int PRIORITY = PRIORITY_MEMORY_GROUP + 40; - private VmFilter filter = new AlwaysMatchFilter(); - - private ApplicationService appSvc; - private VmMemoryStatDAO vmMemoryStatDao; - private AgentInfoDAO agentDAO; - private GCRequest gcRequest; - - public MemoryStatsService(ApplicationService appSvc, VmMemoryStatDAO vmMemoryStatDao, AgentInfoDAO agentDAO, GCRequest gcRequest) { - this.appSvc = appSvc; - this.vmMemoryStatDao = vmMemoryStatDao; - this.gcRequest = gcRequest; - this.agentDAO = agentDAO; - } - - @Override - public VmInformationServiceController getInformationServiceController(VmRef ref) { - MemoryStatsViewProvider viewProvider = OSGIUtils.getInstance().getService(MemoryStatsViewProvider.class); - return new MemoryStatsController(appSvc, vmMemoryStatDao, ref, viewProvider, agentDAO, gcRequest); - } - - @Override - public VmFilter getFilter() { - return filter; - } - - @Override - public int getPriority() { - return PRIORITY; - } -}
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsView.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +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.stats.memory.core; - -import com.redhat.thermostat.client.core.views.BasicView; -import com.redhat.thermostat.client.core.views.UIComponent; -import com.redhat.thermostat.common.ActionListener; -import com.redhat.thermostat.gc.remote.common.command.GCCommand; - -public abstract class MemoryStatsView extends BasicView implements UIComponent { - - public abstract void addRegion(Payload region); - public abstract void updateRegion(Payload region); - - public abstract void addGCActionListener(ActionListener<GCCommand> listener); - - public abstract void requestRepaint(); -}
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsViewProvider.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +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.stats.memory.core; - -import com.redhat.thermostat.client.core.views.ViewProvider; - -public interface MemoryStatsViewProvider extends ViewProvider { - - @Override - public MemoryStatsView createView(); - -}
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/Payload.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,145 +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.stats.memory.core; - -import com.redhat.thermostat.common.utils.DisplayableValues.Scale; - -public class Payload implements Cloneable { - - private String name; - private String tooltip; - - private double capacity; - private double maxCapacity; - private double maxUsed; - private double used; - - private Scale usedUnit; - private Scale capacityUnit; - - private StatsModel model; - - public void setModel(StatsModel model) { - this.model = model; - } - - public StatsModel getModel() { - return model; - } - - public void setCapacityUnit(Scale capacityUnit) { - this.capacityUnit = capacityUnit; - } - - public Scale getCapacityUnit() { - return capacityUnit; - } - - public void setUsedUnit(Scale usedUnit) { - this.usedUnit = usedUnit; - } - - public Scale getUsedUnit() { - return usedUnit; - } - - public double getMaxCapacity() { - return maxCapacity; - } - - public void setMaxCapacity(double maxCapacity) { - this.maxCapacity = maxCapacity; - } - - public double getUsed() { - return used; - } - - public void setUsed(double used) { - this.used = used; - } - - public double getMaxUsed() { - return maxUsed; - } - - public void setMaxUsed(double maxUsed) { - this.maxUsed = maxUsed; - } - - public double getCapacity() { - return capacity; - } - - public void setCapacity(double capacity) { - this.capacity = capacity; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getTooltip() { - return tooltip; - } - - public void setTooltip(String tooltip) { - this.tooltip = tooltip; - } - - @Override - protected Payload clone() { - - Payload copy = new Payload(); - - copy.used = used; - copy.capacity = capacity; - copy.capacityUnit = capacityUnit; - copy.maxCapacity = maxCapacity; - copy.maxUsed = maxUsed; - copy.model = model.clone(); - copy.name = name; - copy.tooltip = tooltip; - copy.usedUnit = usedUnit; - - return copy; - } -}
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/RangeModel.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +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.stats.memory.core; - -public class RangeModel { - - private int minNormalized; - private double min; - - private int maxNormalized; - private double max; - - private double value; - - public double getMinimum() { - return min; - } - - public void setMinimum(double newMinimum) { - this.min = newMinimum; - } - - public double getMaximum() { - return max; - } - - public void setMaximum(double newMaximum) { - this.max = newMaximum; - } - - public void setMaxNormalized(int maxNormalized) { - this.maxNormalized = maxNormalized; - } - - public void setMinNormalized(int minNormalized) { - this.minNormalized = minNormalized; - } - - public double getValue() { - return value; - } - - public void setValue(double newValue) { - this.value = newValue; - } - - int getMaxNormalized() { - return maxNormalized; - } - - int getMinNormalized() { - return minNormalized; - } - - public int getValueNormalized() { - double normalized = ((value - min) * (maxNormalized - minNormalized)/(max - min)) + minNormalized; - return (int) Math.round(normalized); - } -}
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/StatsModel.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +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.stats.memory.core; - -import java.awt.Graphics2D; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.util.Date; - -import javax.swing.plaf.ColorUIResource; - -import org.jfree.chart.JFreeChart; -import org.jfree.chart.axis.DateAxis; -import org.jfree.chart.axis.NumberAxis; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.StandardXYItemRenderer; -import org.jfree.data.time.Millisecond; -import org.jfree.data.time.TimeSeries; -import org.jfree.data.time.TimeSeriesCollection; -import org.jfree.data.xy.XYDataset; -import org.jfree.ui.RectangleInsets; - -public class StatsModel implements Cloneable { - - private static final String lock = new String("MemoryStatsModelLock"); - - private String name; - - private TimeSeries dataSet; - - public StatsModel() { - dataSet = new TimeSeries(""); - } - - BufferedImage getChart(int width, int height, ColorUIResource bgColor, ColorUIResource fgColor) { - JFreeChart chart = createChart(bgColor, fgColor); - - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - chart.draw((Graphics2D) image.getGraphics(), new Rectangle2D.Double(0, 0, width, height), null); - - return image; - } - - public String getName() { - return name; - } - - public void setName(String name) { - dataSet.setDescription(name); - this.name = name; - } - - public void setRange(int seconds) { - dataSet.setMaximumItemCount(seconds); - } - - public void addData(long timestamp, double value) { - Millisecond millisecond = new Millisecond(new Date(timestamp)); - synchronized (lock) { - if (dataSet.getValue(millisecond) == null) { - dataSet.add(millisecond, value); - dataSet.removeAgedItems(true); - } - } - } - - @Override - protected StatsModel clone() { - - StatsModel model = new StatsModel(); - model.setName(name); - model.setRange(dataSet.getMaximumItemCount()); - - try { - model.dataSet = dataSet.createCopy(0, dataSet.getItemCount() - 1); - } catch (CloneNotSupportedException e) { - // ah... it's supported here... - e.printStackTrace(); - } - - return model; - } - - /** - * Creates a chart. - * - * @return a chart. - */ - private JFreeChart createChart(ColorUIResource bgColor, ColorUIResource fgColor) { - - XYDataset priceData = null; - synchronized (lock) { - try { - priceData = new TimeSeriesCollection(dataSet.createCopy(0, - dataSet.getItemCount() - 1)); - } catch (CloneNotSupportedException e) { - e.printStackTrace(); - } - } - - XYPlot plot = new XYPlot(); - - plot.setDomainGridlinesVisible(false); - plot.setDomainCrosshairVisible(false); - plot.setRangeGridlinesVisible(false); - plot.setRangeCrosshairVisible(false); - - DateAxis dateAxis = new DateAxis(); - - dateAxis.setTickLabelsVisible(false); - dateAxis.setTickMarksVisible(false); - dateAxis.setAxisLineVisible(false); - dateAxis.setNegativeArrowVisible(false); - dateAxis.setPositiveArrowVisible(false); - dateAxis.setVisible(false); - - NumberAxis numberAxis = new NumberAxis(); - numberAxis.setTickLabelsVisible(false); - numberAxis.setTickMarksVisible(false); - numberAxis.setAxisLineVisible(false); - numberAxis.setNegativeArrowVisible(false); - numberAxis.setPositiveArrowVisible(false); - numberAxis.setVisible(false); - numberAxis.setAutoRangeIncludesZero(false); - - plot.setDomainAxis(dateAxis); - plot.setRangeAxis(numberAxis); - plot.setDataset(priceData); - - plot.setInsets(new RectangleInsets(-1, -1, 0, 0)); - - plot.setRenderer(new StandardXYItemRenderer(StandardXYItemRenderer.LINES)); - plot.setBackgroundPaint(bgColor); - - JFreeChart chart = new JFreeChart(null, JFreeChart.DEFAULT_TITLE_FONT, plot, false); - - plot.getRenderer().setSeriesPaint(0, fgColor); - chart.setAntiAlias(true); - chart.setBorderVisible(false); - - return chart; - } - - TimeSeries getDataSet() { - return dataSet; - } -}
--- a/client/memory-stats-panel/core/src/main/java/com/redhat/thermostat/client/stats/memory/core/locale/LocaleResources.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +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.stats.memory.core.locale; - -import com.redhat.thermostat.common.locale.Translate; - -public enum LocaleResources { - - VM_INFO_TAB_MEMORY, - RESOURCE_MISSING; - - public static final String RESOURCE_BUNDLE = - "com.redhat.thermostat.client.stats.memory.locale.strings"; - - public static Translate<LocaleResources> createLocalizer() { - return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); - } -}
--- a/client/memory-stats-panel/core/src/main/resources/com/redhat/thermostat/client/stats/memory/locale/strings.properties Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -RESOURCE_MISSING = Missing translation! -VM_INFO_TAB_MEMORY = Memory \ No newline at end of file
--- a/client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/MemoryStatsControllerTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,285 +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.stats.memory.core; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.isA; -import static org.mockito.Mockito.doNothing; -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.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -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.ApplicationService; -import com.redhat.thermostat.common.Timer; -import com.redhat.thermostat.common.Timer.SchedulingType; -import com.redhat.thermostat.common.TimerFactory; -import com.redhat.thermostat.common.dao.AgentInfoDAO; -import com.redhat.thermostat.common.dao.VmMemoryStatDAO; -import com.redhat.thermostat.common.dao.VmRef; -import com.redhat.thermostat.gc.remote.common.GCRequest; -import com.redhat.thermostat.gc.remote.common.command.GCCommand; -import com.redhat.thermostat.storage.model.VmMemoryStat; -import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; -import com.redhat.thermostat.storage.model.VmMemoryStat.Space; - -public class MemoryStatsControllerTest { - - private Generation[] generations = new Generation[2]; - - private VmMemoryStatDAO memoryStatDao; - private MemoryStatsView view; - private Timer timer; - - private ActionListener<MemoryStatsView.Action> viewListener; - private ActionListener<GCCommand> gcActionListener; - - private MemoryStatsController controller; - - private Space canary; - - private AgentInfoDAO agentDAO; - private GCRequest gcRequest; - - private VmRef ref; - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Before - public void setUp() { - timer = mock(Timer.class); - ArgumentCaptor<Runnable> actionCaptor = ArgumentCaptor.forClass(Runnable.class); - doNothing().when(timer).setAction(actionCaptor.capture()); - - TimerFactory timerFactory = mock(TimerFactory.class); - when(timerFactory.createTimer()).thenReturn(timer); - ApplicationService appSvc = mock(ApplicationService.class); - when(appSvc.getTimerFactory()).thenReturn(timerFactory); - - List<VmMemoryStat> vmInfo = new ArrayList<>(); - - for (int i = 0; i < 2; i++) { - Generation generation = new Generation(); - generation.setName("fluff" + i); - VmMemoryStat.Space[] spaces = new VmMemoryStat.Space[2 + (1 - i)]; - for (int j = 0; j < 2; j++) { - Space space = new Space(); - space.setName("fluffer" + i + j); - space.setUsed(100); - space.setCapacity(1000); - space.setMaxCapacity(10000); - spaces[j] = space; - } - if (i == 0) { - // special payload because the others have all the same values - canary = new Space(); - canary.setName("canary"); - canary.setUsed(1); - canary.setCapacity(2); - canary.setMaxCapacity(3); - spaces[2] = canary; - } - generation.setSpaces(spaces); - generations[i] = generation; - } - - long timestamp = 1; - int vmID = 1; - for (int i = 0; i < 5; i++) { - VmMemoryStat vmMemory = new VmMemoryStat(timestamp++, vmID, generations); - vmInfo.add(vmMemory); - } - - memoryStatDao = mock(VmMemoryStatDAO.class); - when(memoryStatDao.getLatestVmMemoryStats(any(VmRef.class), anyLong())).thenReturn(vmInfo); - - view = mock(MemoryStatsView.class); - MemoryStatsViewProvider viewProvider = mock(MemoryStatsViewProvider.class); - when(viewProvider.createView()).thenReturn(view); - - ArgumentCaptor<ActionListener> viewArgumentCaptor = - ArgumentCaptor.forClass(ActionListener.class); - doNothing().when(view).addActionListener(viewArgumentCaptor.capture()); - - ArgumentCaptor<ActionListener> gcArgumentCaptor = - ArgumentCaptor.forClass(ActionListener.class); - doNothing().when(view).addGCActionListener(gcArgumentCaptor.capture()); - - ref = mock(VmRef.class); - - agentDAO = mock(AgentInfoDAO.class); - gcRequest = mock(GCRequest.class); - - controller = new MemoryStatsController(appSvc, memoryStatDao, ref, viewProvider, agentDAO, gcRequest); - - viewListener = viewArgumentCaptor.getValue(); - gcActionListener = gcArgumentCaptor.getValue(); - } - - @Test - public void testStartStopTimer() { - viewListener.actionPerformed(new ActionEvent<>(view, MemoryStatsView.Action.VISIBLE)); - - verify(timer).start(); - verify(timer).setSchedulingType(SchedulingType.FIXED_RATE); - - viewListener.actionPerformed(new ActionEvent<>(view, MemoryStatsView.Action.HIDDEN)); - - verify(timer).stop(); - } - - @Test - public void testGCInvoked() { - gcActionListener.actionPerformed(new ActionEvent<>(view, GCCommand.REQUEST_GC)); - verify(gcRequest).sendGCRequestToAgent(ref, agentDAO); - } - - @Test - public void testPayloadContainSpaces() { - MemoryStatsController.VMCollector collettor = controller.getCollector(); - collettor.run(); - - Map<String, Payload> regions = controller.getRegions(); - assertEquals(5, regions.size()); - - assertTrue(regions.containsKey("fluffer00")); - assertTrue(regions.containsKey("fluffer01")); - assertTrue(regions.containsKey("fluffer10")); - assertTrue(regions.containsKey("fluffer11")); - - assertTrue(regions.containsKey("canary")); - } - - @Test - public void testValues() { - MemoryStatsController.VMCollector collettor = controller.getCollector(); - collettor.run(); - - Map<String, Payload> regions = controller.getRegions(); - - Payload payload = regions.get("fluffer00"); - assertEquals("fluffer00", payload.getName()); - assertEquals(10000, payload.getMaxCapacity(), 0); - assertEquals(1000, payload.getCapacity(), 0); - assertEquals(100, payload.getUsed(), 0); - - payload = regions.get("canary"); - assertEquals("canary", payload.getName()); - assertEquals(3, payload.getMaxCapacity(), 0); - assertEquals(2, payload.getCapacity(), 0); - assertEquals(1, payload.getUsed(), 0); - - // the value above all ensure the same scale is used - String tooltip = payload.getName() + ": used: " + payload.getUsed() + " " + - payload.getUsedUnit() + ", capacity: " + - payload.getCapacity() + " " + payload.getUsedUnit() + - ", max capacity: " + payload.getMaxCapacity() + " " + - payload.getUsedUnit(); - - assertEquals(tooltip, payload.getTooltip()); - } - - - @Test - public void testTimerFetchesMemoryDataDeltaOnly() { - ArgumentCaptor<Long> timeStampCaptor = ArgumentCaptor.forClass(Long.class); - - final long DATA_TIMESTAMP = System.currentTimeMillis() + 1000000000; - Space space = new Space(); - space.setCapacity(10); - space.setMaxCapacity(20); - space.setUsed(5); - Generation gen = new Generation(); - gen.setName("foobar"); - gen.setSpaces(new Space[] { space }); - VmMemoryStat stat = new VmMemoryStat(); - stat.setTimeStamp(DATA_TIMESTAMP); - stat.setGenerations(new Generation[] { gen }); - - when(memoryStatDao.getLatestVmMemoryStats(isA(VmRef.class), anyLong())).thenReturn(Arrays.asList(stat)); - - Runnable timerAction = controller.getCollector(); - - timerAction.run(); - timerAction.run(); - - verify(memoryStatDao, times(2)).getLatestVmMemoryStats(isA(VmRef.class), timeStampCaptor.capture()); - - long timeStamp1 = timeStampCaptor.getAllValues().get(0); - assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1), timeStamp1); - - long timeStamp2 = timeStampCaptor.getAllValues().get(1); - assertTimeStampIsAround(DATA_TIMESTAMP, timeStamp2); - } - - @Test - public void testTimerFetchesMemoryDataDeltaOnlyEvenWithNoData() { - ArgumentCaptor<Long> timeStampCaptor = ArgumentCaptor.forClass(Long.class); - - Runnable timerAction = controller.getCollector(); - - timerAction.run(); - timerAction.run(); - - verify(memoryStatDao, times(2)).getLatestVmMemoryStats(isA(VmRef.class), timeStampCaptor.capture()); - - long timeStamp1 = timeStampCaptor.getAllValues().get(0); - assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1), timeStamp1); - - long timeStamp2 = timeStampCaptor.getAllValues().get(1); - assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1), timeStamp2); - } - - private void assertTimeStampIsAround(long expected, long actual) { - assertTrue(actual <= expected + 1000); - assertTrue(actual >= expected - 1000); - } -}
--- a/client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/PayloadTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +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.stats.memory.core; - -import static org.junit.Assert.*; - -import org.junit.Test; - -import com.redhat.thermostat.common.utils.DisplayableValues.Scale; - -public class PayloadTest { - - @Test - public void testClone() { - - StatsModel model = new StatsModel(); - model.setName("fluffModel"); - model.setRange(100); - model.addData(500, 2.0); - model.addData(501, 2.1); - - Payload source = new Payload(); - source.setCapacity(10.0); - source.setName("fluff"); - source.setCapacityUnit(Scale.GiB); - source.setMaxCapacity(100.0); - source.setMaxUsed(5.0); - source.setUsed(3.0); - source.setUsedUnit(Scale.MiB); - source.setModel(model); - source.setTooltip("fluffTooltip"); - - Payload cloned = source.clone(); - assertNotSame(cloned, source); - - assertEquals(source.getName(), cloned.getName()); - assertEquals(source.getCapacity(), cloned.getCapacity(), 0); - assertEquals(source.getCapacityUnit(), cloned.getCapacityUnit()); - assertEquals(source.getMaxCapacity(), cloned.getMaxCapacity(), 0); - assertEquals(source.getMaxUsed(), cloned.getMaxUsed(), 0); - assertEquals(source.getTooltip(), cloned.getTooltip()); - assertEquals(source.getUsed(), cloned.getUsed(), 0); - assertEquals(source.getUsedUnit(), cloned.getUsedUnit()); - assertNotSame(source.getModel(), cloned.getModel()); - - assertEquals(source.getModel().getName(), cloned.getModel().getName()); - } -}
--- a/client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/RangeModelTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +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.stats.memory.core; - -import static org.junit.Assert.*; -import junit.framework.Assert; - -import org.junit.Test; - -public class RangeModelTest { - - @Test - public void testSameRange() { - RangeModel model = new RangeModel(); - - model.setMaximum(10); - model.setMinimum(0); - model.setValue(5); - - model.setMaxNormalized(10); - model.setMinNormalized(0); - - - Assert.assertEquals((int) model.getValue(), model.getValueNormalized()); - } - - @Test - public void testDoubleRange() { - RangeModel model = new RangeModel(); - - model.setMaximum(10); - model.setMinimum(0); - model.setValue(5); - - model.setMaxNormalized(20); - model.setMinNormalized(0); - - - Assert.assertEquals(10, model.getValueNormalized()); - } - - @Test - public void testRanges() { - RangeModel model = new RangeModel(); - - model.setMaximum(10); - model.setMinimum(0); - model.setValue(5); - - model.setMaxNormalized(40); - model.setMinNormalized(0); - - Assert.assertEquals(20, model.getValueNormalized()); - - model.setMaxNormalized(60); - model.setMinNormalized(0); - - Assert.assertEquals(30, model.getValueNormalized()); - - model.setMaxNormalized(200); - model.setMinNormalized(100); - - Assert.assertEquals(150, model.getValueNormalized()); - - model.setMaximum(100); - model.setMinimum(0); - model.setValue(50); - - model.setMaxNormalized(1); - model.setMinNormalized(0); - - Assert.assertEquals(1, model.getValueNormalized()); - - model.setValue(49); - Assert.assertEquals(0, model.getValueNormalized()); - - model.setMaximum(1.0); - model.setMinimum(0.0); - model.setValue(0.5); - - model.setMaxNormalized(100); - model.setMinNormalized(0); - - Assert.assertEquals(50, model.getValueNormalized()); - - model.setValue(0.72); - Assert.assertEquals(72, model.getValueNormalized()); - } -}
--- a/client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/StatsModelTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +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.stats.memory.core; - -import static org.junit.Assert.*; - -import org.jfree.data.time.TimeSeries; -import org.junit.Test; - -public class StatsModelTest { - - @Test - public void testClone() { - - StatsModel source = new StatsModel(); - source.setName("fluffModel"); - source.setRange(100); - source.addData(500, 2.0); - source.addData(501, 2.1); - - StatsModel cloned = source.clone(); - - assertNotSame(cloned, source); - - assertEquals(source.getName(), cloned.getName()); - - assertNotSame(cloned.getDataSet(), source.getDataSet()); - - for (Object series : cloned.getDataSet().getItems()) { - assertTrue(source.getDataSet().getItems().contains(series)); - } - - assertEquals(cloned.getDataSet().getItemCount(), - source.getDataSet().getItemCount()); - } - -}
--- a/client/memory-stats-panel/core/src/test/java/com/redhat/thermostat/client/stats/memory/core/locale/TranslateTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +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.stats.memory.core.locale; - -import java.io.IOException; -import java.util.Locale; -import java.util.Properties; - -import junit.framework.Assert; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.redhat.thermostat.client.stats.memory.core.locale.LocaleResources; -import com.redhat.thermostat.common.locale.Translate; - -public class TranslateTest { - - private Locale lang; - - @Before - public void setUp() { - this.lang = Locale.getDefault(); - Locale.setDefault(Locale.US); - } - - @After - public void tearDown() { - Locale.setDefault(lang); - } - - @Test - public void verifyTranslationsAreThere() throws IOException { - - String stringsResource = "/" + LocaleResources.RESOURCE_BUNDLE.replace(".", "/") + ".properties"; - - Properties props = new Properties(); - props.load(getClass().getResourceAsStream(stringsResource)); - - Assert.assertEquals(LocaleResources.values().length, props.values().size()); - Translate<LocaleResources> t = LocaleResources.createLocalizer(); - Assert.assertEquals("Missing translation!", t.localize(LocaleResources.RESOURCE_MISSING)); - Assert.assertEquals("Memory", t.localize(LocaleResources.VM_INFO_TAB_MEMORY)); - } -}
--- a/client/memory-stats-panel/pom.xml Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - 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. - ---> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <parent> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-client</artifactId> - <version>0.5.0-SNAPSHOT</version> - </parent> - - <artifactId>thermostat-osgi-memory-stats-panel</artifactId> - <packaging>pom</packaging> - - <name>Thermostat Client VM Memory Monitor plugin</name> - - <modules> - <module>core</module> - <module>swing</module> - </modules> - -</project>
--- a/client/memory-stats-panel/swing/pom.xml Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <artifactId>thermostat-osgi-memory-stats-panel</artifactId> - <groupId>com.redhat.thermostat</groupId> - <version>0.5.0-SNAPSHOT</version> - </parent> - <artifactId>thermostat-osgi-memory-stats-panel-swing</artifactId> - <packaging>bundle</packaging> - <name>Thermostat Client VM Memory Monitor Swing plugin</name> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <extensions>true</extensions> - <configuration> - <instructions> - <Private-Package> - com.redhat.thermostat.client.stats.memory.swing - </Private-Package> - <Bundle-Activator>com.redhat.thermostat.client.stats.memory.swing.MemoryStatsPanelActivator</Bundle-Activator> - <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> - <Bundle-SymbolicName>com.redhat.thermostat.client.stats.memory.swing</Bundle-SymbolicName> - <!-- Do not autogenerate uses clauses in Manifests --> - <_nouses>true</_nouses> - </instructions> - </configuration> - </plugin> - </plugins> - </build> - <dependencies> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.easytesting</groupId> - <artifactId>fest-swing</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>net.java.openjdk.cacio</groupId> - <artifactId>cacio-tta</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.osgi</groupId> - <artifactId>org.osgi.core</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.osgi</groupId> - <artifactId>org.osgi.compendium</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.jfree</groupId> - <artifactId>jfreechart</artifactId> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-common-core</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-osgi-memory-stats-panel-core</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-client-swing</artifactId> - <version>${project.version}</version> - </dependency> - - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-gc-remote-collector-client-common</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-gc-remote-collector-client-swing</artifactId> - <version>${project.version}</version> - </dependency> - - </dependencies> -</project>
--- a/client/memory-stats-panel/swing/src/main/java/com/redhat/thermostat/client/stats/memory/swing/MemoryGraphPanel.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +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.stats.memory.swing; - -import java.awt.Dimension; -import java.beans.Transient; - -import javax.swing.BoxLayout; -import javax.swing.JPanel; - -import com.redhat.thermostat.client.stats.memory.core.MemoryMeter; -import com.redhat.thermostat.client.stats.memory.core.Payload; - -@SuppressWarnings("serial") -class MemoryGraphPanel extends JPanel { - - private MemoryMeter meter; - - /** - * Create the panel. - */ - public MemoryGraphPanel() { - setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); - meter = new MemoryMeter(); - add(meter); - } - - public void setMemoryGraphProperties(Payload region) { - - meter.getPrimaryModel().setMinimum(0); - meter.getPrimaryModel().setMaximum(region.getMaxUsed()); - meter.getPrimaryModel().setValue(region.getUsed()); - - meter.getSecondaryModel().setMinimum(0); - meter.getSecondaryModel().setMaximum(region.getMaxCapacity()); - meter.getSecondaryModel().setValue(region.getCapacity()); - - meter.setToolTipText(region.getTooltip()); - - meter.setPrimaryScaleUnit(region.getUsedUnit().toString()); - meter.setSecondayScaleUnit(region.getCapacityUnit().toString()); - - meter.setStats(region.getModel()); - } - - @Override - @Transient - public Dimension getPreferredSize() { - return meter.getPreferredSize(); - } -}
--- a/client/memory-stats-panel/swing/src/main/java/com/redhat/thermostat/client/stats/memory/swing/MemoryStatsPanelActivator.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +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.stats.memory.swing; - -import java.util.Map; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; - -import com.redhat.thermostat.client.core.VmInformationService; -import com.redhat.thermostat.client.stats.memory.core.MemoryStatsService; -import com.redhat.thermostat.client.stats.memory.core.MemoryStatsViewProvider; -import com.redhat.thermostat.common.ApplicationService; -import com.redhat.thermostat.common.MultipleServiceTracker; -import com.redhat.thermostat.common.MultipleServiceTracker.Action; -import com.redhat.thermostat.common.dao.AgentInfoDAO; -import com.redhat.thermostat.common.dao.VmMemoryStatDAO; -import com.redhat.thermostat.gc.remote.common.GCRequest; - -public class MemoryStatsPanelActivator implements BundleActivator { - - private MultipleServiceTracker tracker; - private ServiceRegistration memoryStatRegistration; - - @Override - public void start(final BundleContext context) throws Exception { - MemoryStatsViewProvider provider = new SwingMemoryStatsViewProvider(); - context.registerService(MemoryStatsViewProvider.class.getName(), provider, null); - - Class<?>[] deps = new Class<?>[] { - ApplicationService.class, - VmMemoryStatDAO.class, - GCRequest.class, - AgentInfoDAO.class, - }; - - tracker = new MultipleServiceTracker(context, deps, new Action() { - - @Override - public void dependenciesUnavailable() { - memoryStatRegistration.unregister(); - memoryStatRegistration = null; - } - - @Override - public void dependenciesAvailable(Map<String, Object> services) { - VmMemoryStatDAO memoryStatDao = (VmMemoryStatDAO) services.get(VmMemoryStatDAO.class.getName()); - AgentInfoDAO agentDAO = (AgentInfoDAO) services.get(AgentInfoDAO.class.getName()); - GCRequest gcRequest = (GCRequest) services.get(GCRequest.class.getName()); - ApplicationService appSvc = (ApplicationService) services.get(ApplicationService.class.getName()); - - MemoryStatsService impl = new MemoryStatsService(appSvc, memoryStatDao, agentDAO, gcRequest); - memoryStatRegistration = context.registerService(VmInformationService.class.getName(), impl , null); - } - }); - tracker.open(); - - } - - @Override - public void stop(BundleContext context) throws Exception { - tracker.close(); - } -}
--- a/client/memory-stats-panel/swing/src/main/java/com/redhat/thermostat/client/stats/memory/swing/MemoryStatsViewImpl.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +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.stats.memory.swing; - -import java.awt.Component; -import java.awt.Dimension; -import java.beans.Transient; -import java.util.HashMap; -import java.util.Map; - -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; - -import com.redhat.thermostat.client.core.views.BasicView; -import com.redhat.thermostat.client.stats.memory.core.MemoryStatsView; -import com.redhat.thermostat.client.stats.memory.core.Payload; -import com.redhat.thermostat.client.swing.SwingComponent; -import com.redhat.thermostat.client.swing.components.HeaderPanel; -import com.redhat.thermostat.client.ui.ComponentVisibleListener; -import com.redhat.thermostat.common.ActionListener; -import com.redhat.thermostat.gc.remote.client.common.RequestGCAction; -import com.redhat.thermostat.gc.remote.client.swing.ToolbarGCButton; -import com.redhat.thermostat.gc.remote.common.command.GCCommand; - -public class MemoryStatsViewImpl extends MemoryStatsView implements SwingComponent { - - private static final long REPAINT_DELAY = 500; - private long lastRepaint; - - private HeaderPanel visiblePanel; - private JPanel realPanel; - - private final Map<String, MemoryGraphPanel> regions; - - private RequestGCAction toobarButtonAction; - - private Dimension preferredSize; - - public MemoryStatsViewImpl() { - super(); - visiblePanel = new HeaderPanel(); - regions = new HashMap<>(); - - preferredSize = new Dimension(0, 0); - - visiblePanel.setHeader("Memory Regions"); - - visiblePanel.addHierarchyListener(new ComponentVisibleListener() { - @Override - public void componentShown(Component component) { - notifier.fireAction(Action.VISIBLE); - } - - @Override - public void componentHidden(Component component) { - notifier.fireAction(Action.HIDDEN); - } - }); - - realPanel = new JPanel(); - realPanel.setLayout(new BoxLayout(realPanel, BoxLayout.Y_AXIS)); - visiblePanel.setContent(realPanel); - - toobarButtonAction = new RequestGCAction(); - visiblePanel.addToolBarButton(new ToolbarGCButton(toobarButtonAction)); - } - - @Transient - public Dimension getPreferredSize() { - return new Dimension(preferredSize); - } - - @Override - public void updateRegion(final Payload region) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - MemoryGraphPanel memoryGraphPanel = regions.get(region.getName()); - memoryGraphPanel.setMemoryGraphProperties(region); - } - }); - } - - @Override - public void addGCActionListener(ActionListener<GCCommand> listener) { - toobarButtonAction.addActionListener(listener); - } - - @Override - public void addRegion(final Payload region) { - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - MemoryGraphPanel memoryGraphPanel = new MemoryGraphPanel(); - - realPanel.add(memoryGraphPanel); - realPanel.add(Box.createRigidArea(new Dimension(5,5))); - regions.put(region.getName(), memoryGraphPanel); - - // components are stacked up vertically in this panel - Dimension memoryGraphPanelMinSize = memoryGraphPanel.getMinimumSize(); - preferredSize.height += memoryGraphPanelMinSize.height + 5; - if (preferredSize.width < (memoryGraphPanelMinSize.width + 5)) { - preferredSize.width = memoryGraphPanelMinSize.width + 5; - } - - updateRegion(region); - realPanel.revalidate(); - } - }); - } - - @Override - public Component getUiComponent() { - return visiblePanel; - } - - @Override - public void requestRepaint() { - // really only repaint every REPAINT_DELAY milliseconds - long now = System.currentTimeMillis(); - if (now - lastRepaint > REPAINT_DELAY) { - visiblePanel.repaint(); - lastRepaint = System.currentTimeMillis(); - } - } - - public BasicView getView() { - return this; - } -}
--- a/client/memory-stats-panel/swing/src/main/java/com/redhat/thermostat/client/stats/memory/swing/SwingMemoryStatsViewProvider.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +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.stats.memory.swing; - -import com.redhat.thermostat.client.stats.memory.core.MemoryStatsView; -import com.redhat.thermostat.client.stats.memory.core.MemoryStatsViewProvider; - -public class SwingMemoryStatsViewProvider implements MemoryStatsViewProvider { - - @Override - public MemoryStatsView createView() { - return new MemoryStatsViewImpl(); - } - -}
--- a/client/memory-stats-panel/swing/src/main/resources/com/redhat/thermostat/client/stats/memory/locale/strings.properties Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -RESOURCE_MISSING = Missing translation! -VM_INFO_TAB_MEMORY = Memory \ No newline at end of file
--- a/client/pom.xml Mon Dec 10 14:30:36 2012 +0100 +++ b/client/pom.xml Mon Dec 10 15:05:37 2012 +0100 @@ -61,7 +61,6 @@ <modules> <module>core</module> <module>heapdumper</module> - <module>memory-stats-panel</module> <module>living-vm-filter</module> <module>command</module> <module>swing</module>
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/MainWindow.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/MainWindow.java Mon Dec 10 15:05:37 2012 +0100 @@ -103,6 +103,7 @@ import com.redhat.thermostat.client.swing.components.EdtHelper; import com.redhat.thermostat.client.swing.components.HtmlTextBuilder; import com.redhat.thermostat.client.swing.components.StatusBar; +import com.redhat.thermostat.client.swing.components.ThermostatPopupMenu; import com.redhat.thermostat.client.swing.internal.MainView; import com.redhat.thermostat.client.swing.internal.components.DecoratedDefaultMutableTreeNode; import com.redhat.thermostat.client.swing.views.SearchFieldSwingView; @@ -326,7 +327,7 @@ private ActionNotifier<Action> actionNotifier = new ActionNotifier<>(this); - private JPopupMenu vmContextMenu; + private ThermostatPopupMenu vmContextMenu; private StatusBar statusBar; private final DefaultMutableTreeNode publishedRoot = @@ -501,7 +502,7 @@ } private void registerContextActionListener(JTree agentVmTree2) { - vmContextMenu = new JPopupMenu(); + vmContextMenu = new ThermostatPopupMenu(); agentVmTree2.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) {
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/HeaderPanel.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/HeaderPanel.java Mon Dec 10 15:05:37 2012 +0100 @@ -179,7 +179,7 @@ hasButtons = true; } - class PreferencesPopup extends JPopupMenu { + class PreferencesPopup extends ThermostatPopupMenu { JMenuItem preferencesMenu; public PreferencesPopup() { // TODO: localize
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatPopupMenu.java Mon Dec 10 15:05:37 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.swing.components; + +import java.awt.Window; + +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; + +@SuppressWarnings("serial") +public class ThermostatPopupMenu extends JPopupMenu { + + @Override + public void setVisible(boolean visible) { + + if (TranslucencyUtils.TRANSLUCENT && !visible) { + setTranslucencyLevel(TranslucencyUtils.OPAQUE); + } + + super.setVisible(visible); + + if (TranslucencyUtils.TRANSLUCENT && visible) { + setTranslucencyLevel(TranslucencyUtils.TRANSPARENCY); + } + } + + private void setTranslucencyLevel(float level) { + try { + Window ourWindow = SwingUtilities.getWindowAncestor(this); + ourWindow.setOpacity(level); + + } catch (Throwable ignore) {} + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/TranslucencyUtils.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,72 @@ +/* + * 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.swing.components; + +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.GraphicsDevice.WindowTranslucency; + +class TranslucencyUtils { + public static final boolean TRANSLUCENT; + public static final float TRANSPARENCY; + public static final float OPAQUE = 1.0f; + + static { + ThermostatPopupMenu.setDefaultLightWeightPopupEnabled(false); + + boolean translucent = !Boolean.getBoolean("com.redhat.thermostat.popup.opaque"); + if (translucent) { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + translucent = gd.isWindowTranslucencySupported(WindowTranslucency.PERPIXEL_TRANSLUCENT); + } + + float transparency = 0.90f; + String transparencyProp = System.getProperty("com.redhat.thermostat.popup.transparency"); + if (transparencyProp != null) { + try { + transparency = Float.parseFloat(transparencyProp); + if (transparency > 1 || transparency < 0) { + translucent = false; + } + } catch (Throwable ignore) {} + } + + TRANSLUCENT = translucent; + TRANSPARENCY = transparency; + } +}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ValueField.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ValueField.java Mon Dec 10 15:05:37 2012 +0100 @@ -38,6 +38,7 @@ import javax.swing.JEditorPane; import javax.swing.UIManager; +import javax.swing.text.DefaultCaret; /** * A custom swing component meant for showing values. Use it like you would use @@ -54,6 +55,17 @@ setForeground(UIManager.getColor("Label.foreground")); setFont(UIManager.getFont("Label.font")); setEditable(false); + + /* + * The default caret update policy forces any scroll pane this + * component is added to to scroll so that this component is visible. + * Normally, the caret is placed in the last instance of this + * component created which is normally at the bottom of a scroll pane. + * This forces the scroll pane to scroll to the bottom, unexpectedly. + * This field is not meant to be editable in the first place so this + * behaviour makes no sense; turn off the scroll updates. + */ + ((DefaultCaret) getCaret()).setUpdatePolicy(DefaultCaret.NEVER_UPDATE); } }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/Main.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/Main.java Mon Dec 10 15:05:37 2012 +0100 @@ -121,8 +121,12 @@ } } + // Thermostat JPopupMenu instances should all be + // ThermostatPopupmenu, so this is redundant, but done in case + // some client code doesn't use the internal popup + JPopupMenu.setDefaultLightWeightPopupEnabled(false); + // TODO: move them in an appropriate place - JPopupMenu.setDefaultLightWeightPopupEnabled(false); UIManager.getDefaults().put("OptionPane.buttonOrientation", SwingConstants.RIGHT); UIManager.getDefaults().put("OptionPane.isYesLast", true); UIManager.getDefaults().put("OptionPane.sameSizeButtons", true);
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/views/HostInformationPanel.java Mon Dec 10 14:30:36 2012 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/views/HostInformationPanel.java Mon Dec 10 15:05:37 2012 +0100 @@ -43,7 +43,6 @@ import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; -import com.redhat.thermostat.client.core.views.BasicView; import com.redhat.thermostat.client.core.views.HostInformationView; import com.redhat.thermostat.client.core.views.UIComponent; import com.redhat.thermostat.client.swing.SwingComponent;
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/Response.java Mon Dec 10 14:30:36 2012 +0100 +++ b/common/command/src/main/java/com/redhat/thermostat/common/command/Response.java Mon Dec 10 15:05:37 2012 +0100 @@ -36,20 +36,31 @@ package com.redhat.thermostat.common.command; - - - public class Response implements Message { // TODO add parameter support to provide more information in some of these types. public enum ResponseType implements MessageType { - PONG, // Just here as response to PING. - OK, // Request has been acknowledged and completed agent-side. - NOK, // Request has been acknowledged and refused agent-side. - NOOP, // Request has been acknowledged, but no action deemed necessary agent-side. - ERROR, // An error on the agent side. - EXCEPTION, // Exception caught by channel handler. Agent-side status unknown. - AUTH_FAILED; // When authentication fails in SecureStorage. + /** Request has been acknowledged and completed agent-side */ + OK, + + /** Request has been acknowledged and refused agent-side. */ + NOK, + + /** + * Request has been acknowledged, but no action deemed necessary + * agent-side. + */ + NOOP, + + /** + * An error occurred. The status of the request is not known. + */ + ERROR, + + /** + * When authentication fails in SecureStorage. + */ + AUTH_FAILED; } ResponseType type;
--- a/common/command/src/test/java/com/redhat/thermostat/common/command/ResponseTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/common/command/src/test/java/com/redhat/thermostat/common/command/ResponseTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -47,8 +47,8 @@ @Test public void testGetType() { - Response r = new Response(ResponseType.PONG); - assertEquals(ResponseType.PONG, r.getType()); + Response r = new Response(ResponseType.OK); + assertEquals(ResponseType.OK, r.getType()); r = new Response(ResponseType.ERROR); assertEquals(ResponseType.ERROR, r.getType()); }
--- a/common/core/src/main/java/com/redhat/thermostat/common/utils/LoggingUtils.java Mon Dec 10 14:30:36 2012 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/utils/LoggingUtils.java Mon Dec 10 15:05:37 2012 +0100 @@ -170,6 +170,7 @@ @SuppressWarnings("rawtypes") Class clazz = ClassLoader.getSystemClassLoader().loadClass(clazzName); Handler handler = (Handler)clazz.newInstance(); + handler.setLevel(root.getLevel()); root.addHandler(handler); } catch (Exception e) { System.err.print("Could not load log-handler '" + clazzName + "'");
--- a/common/core/src/test/java/com/redhat/thermostat/common/utils/LoggingUtilsTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/common/core/src/test/java/com/redhat/thermostat/common/utils/LoggingUtilsTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -85,7 +85,20 @@ assertEquals(SimpleFormatter.class.getName(), handler.getFormatter().getClass().getName()); assertEquals(Level.WARNING.getName(), rootLogger.getLevel().getName()); assertEquals(Level.OFF.getName(), mongodbLogger.getLevel().getName()); - + } + + @Test + public void testHandlersHaveSameLogLevelAsRoot() throws Exception { + LoggingUtils.loadConfig(GLOBAL_CONFIG); + Logger rootLogger = logManager.getLogger(LoggingUtils.ROOTNAME); + FileHandler handler = (FileHandler)getLogHandler(rootLogger, FileHandler.class); + assertEquals(Level.WARNING, rootLogger.getLevel()); + assertEquals(rootLogger.getLevel(), handler.getLevel()); + LoggingUtils.loadConfig(USER_CONFIG); + rootLogger = logManager.getLogger(LoggingUtils.ROOTNAME); + ConsoleHandler handler2 = (ConsoleHandler)getLogHandler(rootLogger, ConsoleHandler.class); + assertEquals(Level.FINEST, rootLogger.getLevel()); + assertEquals(rootLogger.getLevel(), handler2.getLevel()); } @Test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/distribution/config/client.properties Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,12 @@ +# This file is only used in a Web storage setup and is entirely optional. +# It allows for a user to specify a thermostat specific keystore to be used +# for TLS certificate validation in addition to the default as described in: +# http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager + +# If you'd like to use a keystore file in addition to defaults uncomment the +# following line: +#KEYSTORE_FILE=/path/to/thermostat.keystore + +# The password for the keystore file. If none is provided the empty password +# is assumed. Only used if KEYSTORE_FILE was specified. +#KEYSTORE_PASSWORD=nopassword \ No newline at end of file
--- a/distribution/config/commands/gui.properties Mon Dec 10 14:30:36 2012 +0100 +++ b/distribution/config/commands/gui.properties Mon Dec 10 15:05:37 2012 +0100 @@ -24,6 +24,8 @@ thermostat-vm-cpu-client-swing-@project.version@.jar, \ thermostat-vm-gc-client-core-@project.version@.jar, \ thermostat-vm-gc-client-swing-@project.version@.jar, \ + thermostat-vm-memory-client-core-@project.version@.jar, \ + thermostat-vm-memory-client-swing-@project.version@.jar, \ thermostat-client-heapdumper-core-@project.version@.jar, \ thermostat-client-heapdumper-swing-@project.version@.jar, \ thermostat-killvm-client-swing-@project.version@.jar, \ @@ -31,8 +33,6 @@ thermostat-vm-classstat-client-swing-@project.version@.jar, \ thermostat-osgi-living-vm-filter-core-@project.version@.jar, \ thermostat-osgi-living-vm-filter-swing-@project.version@.jar, \ - thermostat-osgi-memory-stats-panel-core-@project.version@.jar, \ - thermostat-osgi-memory-stats-panel-swing-@project.version@.jar, \ thermostat-laf-@project.version@.jar, \ thermostat-thread-collector-@project.version@.jar, \ thermostat-thread-client-swing-@project.version@.jar, \
--- a/distribution/pom.xml Mon Dec 10 14:30:36 2012 +0100 +++ b/distribution/pom.xml Mon Dec 10 15:05:37 2012 +0100 @@ -128,6 +128,14 @@ </resource> <resource> <directory>config</directory> + <targetPath>etc</targetPath> + <filtering>true</filtering> + <includes> + <include>client.properties</include> + </includes> + </resource> + <resource> + <directory>config</directory> <targetPath>storage</targetPath> <filtering>true</filtering> <includes> @@ -409,7 +417,7 @@ </dependency> <dependency> <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-osgi-memory-stats-panel-swing</artifactId> + <artifactId>thermostat-vm-memory-client-swing</artifactId> <version>${project.version}</version> </dependency> <dependency>
--- a/eclipse/com.redhat.thermostat.eclipse/META-INF/MANIFEST.MF Mon Dec 10 14:30:36 2012 +0100 +++ b/eclipse/com.redhat.thermostat.eclipse/META-INF/MANIFEST.MF Mon Dec 10 15:05:37 2012 +0100 @@ -9,8 +9,8 @@ Bundle-ActivationPolicy: lazy Require-Bundle: org.eclipse.core.runtime, org.eclipse.ui -Import-Package: com.redhat.thermostat.client.core.views, - com.redhat.thermostat.client.core.controllers, +Import-Package: com.redhat.thermostat.client.core.controllers, + com.redhat.thermostat.client.core.views, com.redhat.thermostat.client.locale, com.redhat.thermostat.client.ui, com.redhat.thermostat.common, @@ -21,9 +21,7 @@ com.redhat.thermostat.host.overview.client.locale, com.redhat.thermostat.storage.config, com.redhat.thermostat.storage.core, - com.redhat.thermostat.storage.model, - com.redhat.thermostat.web.client, - com.redhat.thermostat.web.common + com.redhat.thermostat.storage.model Export-Package: com.redhat.thermostat.eclipse, com.redhat.thermostat.eclipse.internal;x-friends:="com.redhat.thermostat.eclipse.test,com.redhat.thermostat.eclipse.test.ui", com.redhat.thermostat.eclipse.internal.controllers;x-friends:="com.redhat.thermostat.eclipse.test,com.redhat.thermostat.eclipse.test.ui",
--- a/host-overview/client-swing/src/main/java/com/redhat/thermostat/host/overview/client/swing/HostOverviewPanel.java Mon Dec 10 14:30:36 2012 +0100 +++ b/host-overview/client-swing/src/main/java/com/redhat/thermostat/host/overview/client/swing/HostOverviewPanel.java Mon Dec 10 15:05:37 2012 +0100 @@ -42,6 +42,7 @@ import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.JPanel; +import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.SwingUtilities; @@ -63,6 +64,7 @@ private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); private JPanel visiblePanel; + private JScrollPane scrollPane; private final ValueField hostname = new ValueField("${hostname}"); private final ValueField cpuModel = new ValueField("${cpu-model}"); @@ -202,7 +204,7 @@ @Override public Component getUiComponent() { - return visiblePanel; + return scrollPane; } private void initializePanel() { @@ -304,6 +306,9 @@ panel.add(networkTable); JTableHeader header = networkTable.getTableHeader(); panel.add(header, BorderLayout.PAGE_START); + visiblePanel.setLayout(gl_visiblePanel); + + scrollPane = new JScrollPane(visiblePanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); } }
--- a/killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/internal/SwingVMKilledListener.java Mon Dec 10 14:30:36 2012 +0100 +++ b/killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/internal/SwingVMKilledListener.java Mon Dec 10 15:05:37 2012 +0100 @@ -62,11 +62,6 @@ @Override public void fireComplete(Request request, Response response) { switch (response.getType()) { - case EXCEPTION: - logger.log(Level.SEVERE, - "Exception response from kill VM request. Command channel failure?"); - showErrorMessage(t.localize(LocaleResources.KILL_ACTION_EXCEPTION_RESPONSE_MSG)); - break; case ERROR: String vmId = request.getParameter("vm-id"); logger.log(Level.SEVERE, @@ -74,7 +69,6 @@ + vmId); showErrorMessage(t.localize(LocaleResources.KILL_ACTION_ERROR_RESPONSE_MSG, vmId)); break; - case PONG: // fall-through, also OK :) case OK: logger.log(Level.INFO, "VM with id " + request.getParameter("vm-id")
--- a/killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/locale/LocaleResources.java Mon Dec 10 14:30:36 2012 +0100 +++ b/killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/locale/LocaleResources.java Mon Dec 10 15:05:37 2012 +0100 @@ -42,7 +42,6 @@ ACTION_NAME, ACTION_DESCRIPTION, - KILL_ACTION_EXCEPTION_RESPONSE_MSG, KILL_ACTION_ERROR_RESPONSE_MSG, MISSING_INFO;
--- a/killvm/client-swing/src/main/resources/com/redhat/thermostat/killvm/client/locale/strings.properties Mon Dec 10 14:30:36 2012 +0100 +++ b/killvm/client-swing/src/main/resources/com/redhat/thermostat/killvm/client/locale/strings.properties Mon Dec 10 15:05:37 2012 +0100 @@ -1,5 +1,4 @@ ACTION_NAME = Kill Application ACTION_DESCRIPTION = Kill the selected VM Process MISSING_INFO = Missing Information -KILL_ACTION_EXCEPTION_RESPONSE_MSG = Channel Transport Error. Please check your configuration! KILL_ACTION_ERROR_RESPONSE_MSG = Failed to kill VM with pid {0} \ No newline at end of file
--- a/killvm/client-swing/src/test/java/com/redhat/thermostat/killvm/client/internal/SwingVMKilledListenerTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/killvm/client-swing/src/test/java/com/redhat/thermostat/killvm/client/internal/SwingVMKilledListenerTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -53,15 +53,6 @@ public class SwingVMKilledListenerTest { @Test - public void exceptionShowsError() { - ResponseActionListener listener = new ResponseActionListener(); - Request request = mock(Request.class); - Response resp = new Response(ResponseType.EXCEPTION); - listener.fireComplete(request, resp); - assertTrue(listener.isActionPerformed()); - } - - @Test public void errorShowsError() { ResponseActionListener listener = new ResponseActionListener(); Request request = mock(Request.class);
--- a/pom.xml Mon Dec 10 14:30:36 2012 +0100 +++ b/pom.xml Mon Dec 10 15:05:37 2012 +0100 @@ -140,6 +140,7 @@ <module>vm-cpu</module> <module>vm-gc</module> <module>vm-classstat</module> + <module>vm-memory</module> <!-- development related modules --> <module>dev</module> </modules>
--- a/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/MongoStorageProvider.java Mon Dec 10 14:30:36 2012 +0100 +++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/MongoStorageProvider.java Mon Dec 10 15:05:37 2012 +0100 @@ -36,9 +36,6 @@ package com.redhat.thermostat.storage.mongodb; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; - import com.redhat.thermostat.storage.config.StartupConfiguration; import com.redhat.thermostat.storage.core.Storage; import com.redhat.thermostat.storage.core.StorageProvider;
--- a/vm-gc/remote-collector-client-common/src/main/java/com/redhat/thermostat/gc/remote/common/GCRequest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/vm-gc/remote-collector-client-common/src/main/java/com/redhat/thermostat/gc/remote/common/GCRequest.java Mon Dec 10 15:05:37 2012 +0100 @@ -40,6 +40,7 @@ import com.redhat.thermostat.client.command.RequestQueue; import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; import com.redhat.thermostat.common.command.Request.RequestType; import com.redhat.thermostat.common.dao.AgentInfoDAO; import com.redhat.thermostat.common.dao.HostRef; @@ -53,7 +54,7 @@ this.queue = queue; } - public void sendGCRequestToAgent(VmRef vm, AgentInfoDAO agentDAO) { + public void sendGCRequestToAgent(VmRef vm, AgentInfoDAO agentDAO, RequestResponseListener responseListener) { HostRef targetHostRef = vm.getAgent(); @@ -68,6 +69,8 @@ gcRequest.setParameter(GCCommand.class.getName(), GCCommand.REQUEST_GC.name()); gcRequest.setParameter(GCCommand.VM_ID, vm.getIdString()); + gcRequest.addListener(responseListener); + queue.putRequest(gcRequest); }
--- a/vm-gc/remote-collector-client-common/src/test/java/com/redhat/thermostat/gc/remote/common/GCRequestTest.java Mon Dec 10 14:30:36 2012 +0100 +++ b/vm-gc/remote-collector-client-common/src/test/java/com/redhat/thermostat/gc/remote/common/GCRequestTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -48,6 +48,7 @@ import com.redhat.thermostat.client.command.RequestQueue; import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; import com.redhat.thermostat.common.dao.AgentInfoDAO; import com.redhat.thermostat.common.dao.HostRef; import com.redhat.thermostat.common.dao.VmRef; @@ -62,6 +63,7 @@ private GCRequest gcRequest; private Request request; + private RequestResponseListener listener; @Before public void setUp() { @@ -70,6 +72,8 @@ request = mock(Request.class); + listener = mock(RequestResponseListener.class); + HostRef ref = mock(HostRef.class); when(vm.getAgent()).thenReturn(ref); when(vm.getIdString()).thenReturn("123456"); @@ -101,7 +105,7 @@ } }; - gcRequest.sendGCRequestToAgent(vm, agentDAO); + gcRequest.sendGCRequestToAgent(vm, agentDAO, listener); verify(vm).getAgent(); verify(vm).getIdString(); @@ -112,6 +116,7 @@ verify(request).setReceiver(GCCommand.RECEIVER); verify(request).setParameter(GCCommand.class.getName(), GCCommand.REQUEST_GC.name()); verify(request).setParameter(GCCommand.VM_ID, "123456"); + verify(request).addListener(listener); verify(queue).putRequest(request); }
--- a/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCCommandReceiver.java Mon Dec 10 14:30:36 2012 +0100 +++ b/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCCommandReceiver.java Mon Dec 10 15:05:37 2012 +0100 @@ -41,6 +41,7 @@ import com.redhat.thermostat.common.command.Response; import com.redhat.thermostat.common.command.Response.ResponseType; import com.redhat.thermostat.gc.remote.command.internal.GC; +import com.redhat.thermostat.gc.remote.command.internal.GCException; import com.redhat.thermostat.gc.remote.common.command.GCCommand; import com.redhat.thermostat.utils.management.MXBeanConnector; @@ -48,18 +49,22 @@ @Override public Response receive(Request request) { + Response response = new Response(ResponseType.OK); String command = request.getParameter(GCCommand.class.getName()); switch (GCCommand.valueOf(command)) { case REQUEST_GC: - String vmId = request.getParameter(GCCommand.VM_ID); - MXBeanConnector connector = new MXBeanConnector(vmId); - new GC(connector).gc(); + try { + String vmId = request.getParameter(GCCommand.VM_ID); + MXBeanConnector connector = new MXBeanConnector(vmId); + new GC(connector).gc(); + } catch (GCException gce) { + response = new Response(ResponseType.ERROR); + } break; - default: break; } - return new Response(ResponseType.OK); + return response; } }
--- a/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/GC.java Mon Dec 10 14:30:36 2012 +0100 +++ b/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/GC.java Mon Dec 10 15:05:37 2012 +0100 @@ -54,7 +54,8 @@ this.connector = connector; } - public void gc() { + public void gc() throws GCException { + Exception exceptionInGc = null; boolean closeAfter = false; if (!connector.isAttached()) { closeAfter = true; @@ -75,12 +76,17 @@ bean.gc(); } catch (Exception ex) { + exceptionInGc = ex; logger.log(Level.SEVERE, "can't get MXBeanConnection connection", ex); } if (closeAfter) { closeConnection(); } + + if (exceptionInGc != null) { + throw new GCException("error performing gc", exceptionInGc); + } } private void closeConnection() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/GCException.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,44 @@ +/* + * 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.gc.remote.command.internal; + +public class GCException extends Exception { + + public GCException(String message, Throwable cause) { + super(message, cause); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/pom.xml Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>thermostat-vm-memory</artifactId> + <groupId>com.redhat.thermostat</groupId> + <version>0.5.0-SNAPSHOT</version> + </parent> + <artifactId>thermostat-vm-memory-client-core</artifactId> + <packaging>bundle</packaging> + <name>Thermostat VM Memory Core Client plugin</name> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Export-Package> + com.redhat.thermostat.vm.memory.client.core, + com.redhat.thermostat.vm.memory.client.locale, + </Export-Package> + <Private-Package> + com.redhat.thermostat.vm.memory.client.core.internal + </Private-Package> + <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> + <Bundle-Activator>com.redhat.thermostat.vm.memory.client.core.internal.Activator</Bundle-Activator> + <Bundle-SymbolicName>com.redhat.thermostat.vm.memory.client.core</Bundle-SymbolicName> + <!-- Do not autogenerate uses clauses in Manifests --> + <_nouses>true</_nouses> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.jfree</groupId> + <artifactId>jfreechart</artifactId> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-client-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-test</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-gc-remote-collector-client-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-gc-remote-collector-client-common</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryMeter.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,433 @@ +/* + * 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.vm.memory.client.core; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.beans.Transient; + +import javax.swing.JComponent; +import javax.swing.plaf.ColorUIResource; + +import sun.swing.SwingUtilities2; + +@SuppressWarnings({ "restriction", "serial" }) +public class MemoryMeter extends JComponent { + + // TODO the font should be customizable + private static final Font font = new Font("SansSerif", Font.PLAIN, 10); + + private static final int TICK_NUM = 100; + private static final int SMALL_TICK_NUM = 5; + + private static final int MAIN_BAR_HEIGHT = 20; + + private static final ColorUIResource MAIN_GRADIENT_TOP = new ColorUIResource(0xf1f3f1); + private static final ColorUIResource MAIN_BORDER_COLOR = new ColorUIResource(0xa8aca8); + + private static final ColorUIResource MAIN_BAR_BASE_COLOR_TOP = new ColorUIResource(0xbcd5ef); + private static final ColorUIResource MAIN_BAR_BASE_COLOR = new ColorUIResource(0x4A90D9); + + //private static final ColorUIResource STATS_BG = new ColorUIResource(0xF8F8F8); + private static final ColorUIResource STATS_BG = new ColorUIResource(0xFFFFFF); + + private ColorUIResource tickColor; + + private RangeModel primary; + private RangeModel secondary; + + private Insets boundInsets; + + private RangeModel internalSecondaryModel; + + private StatsModel primaryStats; + + private String primaryUnit; + private String secondaryUnit; + + public void setPrimaryScaleUnit(String primaryUnit) { + this.primaryUnit = primaryUnit; + } + + public void setSecondayScaleUnit(String secondaryUnit) { + this.secondaryUnit = secondaryUnit; + } + + public StatsModel getStats() { + return primaryStats; + } + + public void setStats(StatsModel primaryStats) { + this.primaryStats = primaryStats; + } + + public MemoryMeter() { + + secondaryUnit = ""; + primaryUnit = ""; + + boundInsets = new Insets(10, 10, 20, 20); + + tickColor = new ColorUIResource(0xdbdddb); + + primary = new RangeModel(); + primary.setMinimum(0); + primary.setMaximum(100); + primary.setMinNormalized(0); + primary.setMaxNormalized(100); + + secondary = new RangeModel(); + secondary.setMinimum(0); + secondary.setMaximum(100); + secondary.setMinNormalized(0); + secondary.setMaxNormalized(100); + + internalSecondaryModel = new RangeModel(); + } + + public ColorUIResource getTickColor() { + return tickColor; + } + + public void setTickColor(ColorUIResource tickColor) { + this.tickColor = tickColor; + } + + public RangeModel getPrimaryModel() { + return primary; + } + + public RangeModel getSecondaryModel() { + return secondary; + } + + protected Rectangle getOuterBounds() { + return new Rectangle(0, 0, getWidth(), getHeight()); + } + + protected Rectangle getBoundsWithInsets(Rectangle bounds) { + return new Rectangle(bounds.x + boundInsets.left, + bounds.y + boundInsets.top, + bounds.width - boundInsets.right, + bounds.height - boundInsets.bottom); + } + + /** + * paint the outher frame, including the light border sorrounding + */ + protected void paintOuterFrame(Graphics2D graphics, Rectangle bounds) { + + RoundRectangle2D frame = new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height, 6, 6); + + Paint paint = new GradientPaint(0, 0, MAIN_GRADIENT_TOP, 0, getHeight(), getBackground()); + graphics.setPaint(paint); + graphics.fill(frame); + + paint = new GradientPaint(0, 0, MAIN_BORDER_COLOR, 0, getHeight(), getBackground()); + graphics.setPaint(paint); + frame = new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width -1, bounds.height, 6, 6); + graphics.draw(frame); + } + + /** + * paint the track sorrounding the main bar + */ + protected void paintMainBarTrackFill(Graphics2D graphics, Rectangle bounds) { + + Paint paint = new GradientPaint(0, 0, MAIN_GRADIENT_TOP, 0, bounds.height, Color.WHITE); + graphics.setPaint(paint); + RoundRectangle2D frame = new RoundRectangle2D.Float(0, 0, bounds.width, bounds.height, 6, 6); + graphics.fill(frame); + } + + /** + */ + protected void paintMainBarTrackBorder(Graphics2D graphics, Rectangle bounds) { + Paint paint = new GradientPaint(0, 0, MAIN_BORDER_COLOR, getWidth(), 0, getBackground()); + graphics.setPaint(paint); + + RoundRectangle2D frame = new RoundRectangle2D.Float(0, 0, bounds.width - 1, bounds.height, 6, 6); + graphics.draw(frame); + } + + + /** + * this is the main bar, will it up to what is defined by the model + */ + protected void paintMainBarFill(Graphics2D graphics, Rectangle bounds) { + Paint paint = new GradientPaint(0, 0, MAIN_BAR_BASE_COLOR, getWidth() * 2, 0, getBackground()); + graphics.setPaint(paint); + + RoundRectangle2D frame = + new RoundRectangle2D.Float(0, 0, getPrimaryModel().getValueNormalized(), + bounds.height, 6, 6); + graphics.fill(frame); + + String value = String.valueOf(getPrimaryModel().getValue()) + " " + primaryUnit; + Rectangle2D fontBounds = font.getStringBounds(value, graphics.getFontRenderContext()); + int width = (int) (bounds.width/2 - fontBounds.getWidth()/2) - 1; + + if (width > getPrimaryModel().getValueNormalized()) { + graphics.setPaint(MAIN_BAR_BASE_COLOR); + } else { + graphics.setPaint(getBackground()); + } + + int height = (int) (bounds.height / 2 + fontBounds.getHeight()/2); + SwingUtilities2.drawString(this, graphics, value, width, height); + } + + /** + */ + private void paintMainBar(Graphics2D g, Rectangle bounds) { + + Graphics2D graphics = (Graphics2D) g.create(); + + graphics.translate(bounds.x, bounds.y); + paintMainBarTrackFill(graphics, bounds); + + paintMainBarFill(graphics, bounds); + + paintMainBarTrackBorder(graphics, bounds); + graphics.dispose(); + } + + /** + */ + protected void drawBottomBar(Graphics2D g, Rectangle bounds) { + + Graphics2D graphics = (Graphics2D) g.create(); + graphics.translate(bounds.x, bounds.y + bounds.height); + + drawTickMark(graphics, bounds); + + paintSecondaryBarFill(graphics, bounds); + + graphics.dispose(); + } + + /** + */ + protected void drawTickMark(Graphics2D graphics, Rectangle bounds) { + + graphics.setPaint(MAIN_BORDER_COLOR); + + int smallTop = bounds.height - 5; + int smallBottom = bounds.height; + + int mainTop = smallTop + 20; + int mainBottom = smallBottom - 15; + + // the first and last tick are always big, this is the first + graphics.drawLine(0, mainTop, 0, mainBottom); + + // the space between the vertical lines + double tickSpace = ((double) bounds.width) / TICK_NUM; + + internalSecondaryModel.setMaxNormalized(bounds.width); + int numTicks = 0; + + for (int x = 0; x < bounds.width; x += tickSpace + 0.5) { + if (numTicks % SMALL_TICK_NUM == 0) { + + graphics.drawLine(x, smallTop, x, smallBottom + 5); + + internalSecondaryModel.setValue(x); + } else { + graphics.drawLine(x, smallTop, x, smallBottom); + } + numTicks++; + } + + // that's the last + graphics.drawLine(bounds.width, mainTop, bounds.width, mainBottom); + graphics.drawLine(0, smallTop, bounds.width, smallTop); + + drawStrings(graphics, mainBottom, mainTop, bounds.width); + } + + protected void drawStrings(Graphics2D graphics, int top, int bottom, int right) { + + // now draw the min/max values of both side of markers + // top bar min value + FontMetrics fm = SwingUtilities2.getFontMetrics(this, font); + + String value = String.valueOf(getPrimaryModel().getMinimum()) + " " + primaryUnit; + int height = top + fm.getAscent()/2; + SwingUtilities2.drawString(this, graphics, value, 1, height); + + value = String.valueOf(getSecondaryModel().getMinimum()) + " " + secondaryUnit; + height = bottom; + SwingUtilities2.drawString(this, graphics, value, 1, height); + + value = String.valueOf(getPrimaryModel().getMaximum()) + " " + primaryUnit; + height = top + fm.getAscent()/2; + + int width = (int) (right - font.getStringBounds(value, graphics.getFontRenderContext()).getWidth()) - 1; + SwingUtilities2.drawString(this, graphics, value, width, height); + + value = String.valueOf(getSecondaryModel().getMaximum()) + " " + secondaryUnit; + height = bottom; + width = (int) (right - font.getStringBounds(value, graphics.getFontRenderContext()).getWidth()) - 1; + SwingUtilities2.drawString(this, graphics, value, width, height); + + // now draw the actual value for the bottom bar, the top bar is drawn in + // its fill method + value = String.valueOf(getSecondaryModel().getValue()) + " " + secondaryUnit; + width = right/2; + Rectangle2D bounds = font.getStringBounds(value, graphics.getFontRenderContext()); + width = (int) (width - bounds.getWidth()/2) - 1; + SwingUtilities2.drawString(this, graphics, value, width, height); + RoundRectangle2D frame = new RoundRectangle2D.Double(width - 2, height - bounds.getHeight(), + bounds.getWidth() + 4, bounds.getHeight() + 4, + 4, 4); + graphics.draw(frame); + } + + protected void paintSecondaryBarFill(Graphics2D graphics, Rectangle bounds) { + + graphics.setPaint(MAIN_BAR_BASE_COLOR_TOP); + graphics.drawLine(1, bounds.height, getSecondaryModel().getValueNormalized() - 1, bounds.height); + + graphics.setPaint(MAIN_BAR_BASE_COLOR); + graphics.fillRect(1, bounds.height + 1, getSecondaryModel().getValueNormalized(), 2); + } + + protected void paintStats(Graphics2D graphics, Rectangle bounds) { + + int imageWidth = bounds.width - 2; + if (imageWidth < 0 || bounds.height < 0) { + return; + } + + StatsModel stats = getStats(); + drawStats(graphics, stats, bounds.x, bounds.y, imageWidth, bounds.height); + } + + private void drawStats(Graphics2D graphics, StatsModel stats, int x, int y, int imageWidth, int height) { + if (stats != null) { + BufferedImage image = stats.getChart(imageWidth, height, STATS_BG, + new ColorUIResource(getForeground())); + + paintStatsLabel(graphics, image, stats.getName()); + graphics.drawImage(image, x, y, null); + } + } + + protected void paintStatsLabel(Graphics2D graphics, BufferedImage image, String label) { + int height = SwingUtilities2.getFontMetrics(this, font).getAscent() + 2; + if (height <= 0) { + return; + } + + Graphics2D imageGraphics = (Graphics2D) image.getGraphics(); + imageGraphics.setColor(getForeground()); + imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + SwingUtilities2.drawString(this, imageGraphics, label, 2, height); + } + + @Override + protected void paintComponent(Graphics g) { + + Graphics2D graphics = (Graphics2D) g.create(); + graphics.setFont(font); + + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + Rectangle outerBounds = getOuterBounds(); + Rectangle innerBounds = getBoundsWithInsets(outerBounds); + Rectangle statsBound = getBoundsWithInsets(outerBounds); + + // move the bar close to the center + innerBounds.height = MAIN_BAR_HEIGHT; + innerBounds.y = outerBounds.height/2; + + // make the stats area cover the upper portion instead + statsBound.height = (outerBounds.height/2) - MAIN_BAR_HEIGHT; + + resetModels(0, innerBounds.width); + + // some eye candy + paintOuterFrame(graphics, outerBounds); + + // paint the usage stats + paintStats(graphics, statsBound); + + // main and bottom bars + paintMainBar(graphics, innerBounds); + drawBottomBar(graphics, innerBounds); + + graphics.dispose(); + } + + private void resetModels(int min, int max) { + + getPrimaryModel().setMaxNormalized(max); + getPrimaryModel().setMinNormalized(min); + + RangeModel model = getSecondaryModel(); + model.setMaxNormalized(max); + model.setMinNormalized(min); + + internalSecondaryModel.setMaximum(model.getMaximum()); + internalSecondaryModel.setMinimum(model.getMinimum()); + internalSecondaryModel.setValue(model.getValue()); + + internalSecondaryModel.setMaxNormalized(max); + internalSecondaryModel.setMinNormalized(0); + } + + @Override + @Transient + public Dimension getPreferredSize() { + return new Dimension(850, 150); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsService.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,83 @@ +/* + * 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.vm.memory.client.core; + +import com.redhat.thermostat.client.core.VmFilter; +import com.redhat.thermostat.client.core.VmInformationService; +import com.redhat.thermostat.client.core.controllers.VmInformationServiceController; +import com.redhat.thermostat.client.osgi.service.AlwaysMatchFilter; +import com.redhat.thermostat.common.ApplicationService; +import com.redhat.thermostat.common.dao.AgentInfoDAO; +import com.redhat.thermostat.common.dao.VmMemoryStatDAO; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.utils.OSGIUtils; +import com.redhat.thermostat.gc.remote.common.GCRequest; +import com.redhat.thermostat.vm.memory.client.core.internal.MemoryStatsController; + +public class MemoryStatsService implements VmInformationService { + + private static final int PRIORITY = PRIORITY_MEMORY_GROUP + 40; + private VmFilter filter = new AlwaysMatchFilter(); + + private ApplicationService appSvc; + private VmMemoryStatDAO vmMemoryStatDao; + private AgentInfoDAO agentDAO; + private GCRequest gcRequest; + + public MemoryStatsService(ApplicationService appSvc, VmMemoryStatDAO vmMemoryStatDao, AgentInfoDAO agentDAO, GCRequest gcRequest) { + this.appSvc = appSvc; + this.vmMemoryStatDao = vmMemoryStatDao; + this.gcRequest = gcRequest; + this.agentDAO = agentDAO; + } + + @Override + public VmInformationServiceController getInformationServiceController(VmRef ref) { + MemoryStatsViewProvider viewProvider = OSGIUtils.getInstance().getService(MemoryStatsViewProvider.class); + return new MemoryStatsController(appSvc, vmMemoryStatDao, ref, viewProvider, agentDAO, gcRequest); + } + + @Override + public VmFilter getFilter() { + return filter; + } + + @Override + public int getPriority() { + return PRIORITY; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsView.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,55 @@ +/* + * 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.vm.memory.client.core; + +import com.redhat.thermostat.client.core.views.BasicView; +import com.redhat.thermostat.client.core.views.UIComponent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.gc.remote.common.command.GCCommand; + +public abstract class MemoryStatsView extends BasicView implements UIComponent { + + public abstract void addRegion(Payload region); + public abstract void updateRegion(Payload region); + + public abstract void addGCActionListener(ActionListener<GCCommand> listener); + + public abstract void requestRepaint(); + + public abstract void displayWarning(String string); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsViewProvider.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,46 @@ +/* + * 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.vm.memory.client.core; + +import com.redhat.thermostat.client.core.views.ViewProvider; + +public interface MemoryStatsViewProvider extends ViewProvider { + + @Override + public MemoryStatsView createView(); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/Payload.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,145 @@ +/* + * 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.vm.memory.client.core; + +import com.redhat.thermostat.common.utils.DisplayableValues.Scale; + +public class Payload implements Cloneable { + + private String name; + private String tooltip; + + private double capacity; + private double maxCapacity; + private double maxUsed; + private double used; + + private Scale usedUnit; + private Scale capacityUnit; + + private StatsModel model; + + public void setModel(StatsModel model) { + this.model = model; + } + + public StatsModel getModel() { + return model; + } + + public void setCapacityUnit(Scale capacityUnit) { + this.capacityUnit = capacityUnit; + } + + public Scale getCapacityUnit() { + return capacityUnit; + } + + public void setUsedUnit(Scale usedUnit) { + this.usedUnit = usedUnit; + } + + public Scale getUsedUnit() { + return usedUnit; + } + + public double getMaxCapacity() { + return maxCapacity; + } + + public void setMaxCapacity(double maxCapacity) { + this.maxCapacity = maxCapacity; + } + + public double getUsed() { + return used; + } + + public void setUsed(double used) { + this.used = used; + } + + public double getMaxUsed() { + return maxUsed; + } + + public void setMaxUsed(double maxUsed) { + this.maxUsed = maxUsed; + } + + public double getCapacity() { + return capacity; + } + + public void setCapacity(double capacity) { + this.capacity = capacity; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTooltip() { + return tooltip; + } + + public void setTooltip(String tooltip) { + this.tooltip = tooltip; + } + + @Override + public Payload clone() { + + Payload copy = new Payload(); + + copy.used = used; + copy.capacity = capacity; + copy.capacityUnit = capacityUnit; + copy.maxCapacity = maxCapacity; + copy.maxUsed = maxUsed; + copy.model = model.clone(); + copy.name = name; + copy.tooltip = tooltip; + copy.usedUnit = usedUnit; + + return copy; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/RangeModel.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,93 @@ +/* + * 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.vm.memory.client.core; + +public class RangeModel { + + private int minNormalized; + private double min; + + private int maxNormalized; + private double max; + + private double value; + + public double getMinimum() { + return min; + } + + public void setMinimum(double newMinimum) { + this.min = newMinimum; + } + + public double getMaximum() { + return max; + } + + public void setMaximum(double newMaximum) { + this.max = newMaximum; + } + + public void setMaxNormalized(int maxNormalized) { + this.maxNormalized = maxNormalized; + } + + public void setMinNormalized(int minNormalized) { + this.minNormalized = minNormalized; + } + + public double getValue() { + return value; + } + + public void setValue(double newValue) { + this.value = newValue; + } + + int getMaxNormalized() { + return maxNormalized; + } + + int getMinNormalized() { + return minNormalized; + } + + public int getValueNormalized() { + double normalized = ((value - min) * (maxNormalized - minNormalized)/(max - min)) + minNormalized; + return (int) Math.round(normalized); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/StatsModel.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,181 @@ +/* + * 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.vm.memory.client.core; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.Date; + +import javax.swing.plaf.ColorUIResource; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.StandardXYItemRenderer; +import org.jfree.data.time.Millisecond; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; +import org.jfree.data.xy.XYDataset; +import org.jfree.ui.RectangleInsets; + +public class StatsModel implements Cloneable { + + private static final String lock = new String("MemoryStatsModelLock"); + + private String name; + + private TimeSeries dataSet; + + public StatsModel() { + dataSet = new TimeSeries(""); + } + + BufferedImage getChart(int width, int height, ColorUIResource bgColor, ColorUIResource fgColor) { + JFreeChart chart = createChart(bgColor, fgColor); + + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + chart.draw((Graphics2D) image.getGraphics(), new Rectangle2D.Double(0, 0, width, height), null); + + return image; + } + + public String getName() { + return name; + } + + public void setName(String name) { + dataSet.setDescription(name); + this.name = name; + } + + public void setRange(int seconds) { + dataSet.setMaximumItemCount(seconds); + } + + public void addData(long timestamp, double value) { + Millisecond millisecond = new Millisecond(new Date(timestamp)); + synchronized (lock) { + if (dataSet.getValue(millisecond) == null) { + dataSet.add(millisecond, value); + dataSet.removeAgedItems(true); + } + } + } + + @Override + protected StatsModel clone() { + + StatsModel model = new StatsModel(); + model.setName(name); + model.setRange(dataSet.getMaximumItemCount()); + + try { + model.dataSet = dataSet.createCopy(0, dataSet.getItemCount() - 1); + } catch (CloneNotSupportedException e) { + // ah... it's supported here... + e.printStackTrace(); + } + + return model; + } + + /** + * Creates a chart. + * + * @return a chart. + */ + private JFreeChart createChart(ColorUIResource bgColor, ColorUIResource fgColor) { + + XYDataset priceData = null; + synchronized (lock) { + try { + priceData = new TimeSeriesCollection(dataSet.createCopy(0, + dataSet.getItemCount() - 1)); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + } + + XYPlot plot = new XYPlot(); + + plot.setDomainGridlinesVisible(false); + plot.setDomainCrosshairVisible(false); + plot.setRangeGridlinesVisible(false); + plot.setRangeCrosshairVisible(false); + + DateAxis dateAxis = new DateAxis(); + + dateAxis.setTickLabelsVisible(false); + dateAxis.setTickMarksVisible(false); + dateAxis.setAxisLineVisible(false); + dateAxis.setNegativeArrowVisible(false); + dateAxis.setPositiveArrowVisible(false); + dateAxis.setVisible(false); + + NumberAxis numberAxis = new NumberAxis(); + numberAxis.setTickLabelsVisible(false); + numberAxis.setTickMarksVisible(false); + numberAxis.setAxisLineVisible(false); + numberAxis.setNegativeArrowVisible(false); + numberAxis.setPositiveArrowVisible(false); + numberAxis.setVisible(false); + numberAxis.setAutoRangeIncludesZero(false); + + plot.setDomainAxis(dateAxis); + plot.setRangeAxis(numberAxis); + plot.setDataset(priceData); + + plot.setInsets(new RectangleInsets(-1, -1, 0, 0)); + + plot.setRenderer(new StandardXYItemRenderer(StandardXYItemRenderer.LINES)); + plot.setBackgroundPaint(bgColor); + + JFreeChart chart = new JFreeChart(null, JFreeChart.DEFAULT_TITLE_FONT, plot, false); + + plot.getRenderer().setSeriesPaint(0, fgColor); + chart.setAntiAlias(true); + chart.setBorderVisible(false); + + return chart; + } + + TimeSeries getDataSet() { + return dataSet; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/Activator.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,94 @@ +/* + * 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.vm.memory.client.core.internal; + +import java.util.Map; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import com.redhat.thermostat.client.core.VmInformationService; +import com.redhat.thermostat.common.ApplicationService; +import com.redhat.thermostat.common.MultipleServiceTracker; +import com.redhat.thermostat.common.MultipleServiceTracker.Action; +import com.redhat.thermostat.common.dao.AgentInfoDAO; +import com.redhat.thermostat.common.dao.VmMemoryStatDAO; +import com.redhat.thermostat.gc.remote.common.GCRequest; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsService; + +public class Activator implements BundleActivator { + + private MultipleServiceTracker tracker; + private ServiceRegistration memoryStatRegistration; + + @Override + public void start(final BundleContext context) throws Exception { + Class<?>[] deps = new Class<?>[] { + ApplicationService.class, + VmMemoryStatDAO.class, + GCRequest.class, + AgentInfoDAO.class, + }; + + tracker = new MultipleServiceTracker(context, deps, new Action() { + + @Override + public void dependenciesUnavailable() { + memoryStatRegistration.unregister(); + memoryStatRegistration = null; + } + + @Override + public void dependenciesAvailable(Map<String, Object> services) { + VmMemoryStatDAO memoryStatDao = (VmMemoryStatDAO) services.get(VmMemoryStatDAO.class.getName()); + AgentInfoDAO agentDAO = (AgentInfoDAO) services.get(AgentInfoDAO.class.getName()); + GCRequest gcRequest = (GCRequest) services.get(GCRequest.class.getName()); + ApplicationService appSvc = (ApplicationService) services.get(ApplicationService.class.getName()); + + MemoryStatsService impl = new MemoryStatsService(appSvc, memoryStatDao, agentDAO, gcRequest); + memoryStatRegistration = context.registerService(VmInformationService.class.getName(), impl , null); + } + }); + tracker.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + tracker.close(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,261 @@ +/* + * 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.vm.memory.client.core.internal; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import com.redhat.thermostat.client.core.controllers.VmInformationServiceController; +import com.redhat.thermostat.client.core.views.BasicView.Action; +import com.redhat.thermostat.client.core.views.UIComponent; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ApplicationService; +import com.redhat.thermostat.common.NotImplementedException; +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.common.Timer.SchedulingType; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; +import com.redhat.thermostat.common.dao.AgentInfoDAO; +import com.redhat.thermostat.common.dao.VmMemoryStatDAO; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.locale.Translate; +import com.redhat.thermostat.common.utils.DisplayableValues.Scale; +import com.redhat.thermostat.gc.remote.common.GCRequest; +import com.redhat.thermostat.gc.remote.common.command.GCCommand; +import com.redhat.thermostat.storage.model.VmMemoryStat; +import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; +import com.redhat.thermostat.storage.model.VmMemoryStat.Space; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsView; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsViewProvider; +import com.redhat.thermostat.vm.memory.client.core.Payload; +import com.redhat.thermostat.vm.memory.client.core.StatsModel; +import com.redhat.thermostat.vm.memory.client.locale.LocaleResources; + +public class MemoryStatsController implements VmInformationServiceController { + + private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer(); + + private final MemoryStatsView view; + private final VmMemoryStatDAO vmDao; + + private final VmRef ref; + private final Timer timer; + + private final Map<String, Payload> regions; + + private VMCollector collector; + + class VMCollector implements Runnable { + + private long desiredUpdateTimeStamp = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1); + + @Override + public void run() { + List<VmMemoryStat> vmInfo = vmDao.getLatestVmMemoryStats(ref, desiredUpdateTimeStamp); + for (VmMemoryStat memoryStats: vmInfo) { + Generation[] generations = memoryStats.getGenerations(); + + for (Generation generation : generations) { + Space[] spaces = generation.getSpaces(); + for (Space space: spaces) { + Payload payload = regions.get(space.getName()); + if (payload == null) { + payload = new Payload(); + payload.setName(space.getName()); + } + + Scale usedScale = normalizeScale(space.getUsed(), space.getCapacity()); + double used = Scale.convertTo(usedScale, space.getUsed(), 100); + double maxUsed = Scale.convertTo(usedScale, space.getCapacity(), 100); + + payload.setUsed(used); + payload.setMaxUsed(maxUsed); + payload.setUsedUnit(usedScale); + + Scale maxScale = normalizeScale(space.getCapacity(), space.getMaxCapacity()); + double capacity = Scale.convertTo(maxScale, space.getCapacity(), 100); + double maxCapacity = Scale.convertTo(maxScale, space.getMaxCapacity(), 100); + + payload.setCapacity(capacity); + payload.setMaxCapacity(maxCapacity); + payload.setCapacityUnit(maxScale); + + String tooltip = space.getName() + ": used: " + used + " " + usedScale + + ", capacity: " + capacity + " " + maxScale + + ", max capacity: " + maxCapacity + " " + maxScale; + + payload.setTooltip(tooltip); + + StatsModel model = payload.getModel(); + if (model == null) { + model = new StatsModel(); + model.setName(space.getName()); + model.setRange(3600); + } + + // normalize this always in the same unit + model.addData(memoryStats.getTimeStamp(), + Scale.convertTo(Scale.MiB, space.getUsed(), 100)); + + payload.setModel(model); + if (regions.containsKey(space.getName())) { + view.updateRegion(payload.clone()); + } else { + view.addRegion(payload.clone()); + regions.put(space.getName(), payload); + } + + view.requestRepaint(); + desiredUpdateTimeStamp = Math.max(desiredUpdateTimeStamp, memoryStats.getTimeStamp()); + } + } + } + } + } + + public MemoryStatsController(ApplicationService appSvc, final VmMemoryStatDAO vmMemoryStatDao, + final VmRef ref, MemoryStatsViewProvider viewProvider, + final AgentInfoDAO agentDAO, final GCRequest gcRequest) { + + regions = new HashMap<>(); + this.ref = ref; + vmDao = vmMemoryStatDao; + view = viewProvider.createView(); + + timer = appSvc.getTimerFactory().createTimer(); + + collector = new VMCollector(); + timer.setAction(collector); + + timer.setInitialDelay(0); + timer.setDelay(1000); + timer.setTimeUnit(TimeUnit.MILLISECONDS); + timer.setSchedulingType(SchedulingType.FIXED_RATE); + + view.addActionListener(new ActionListener<Action>() { + @Override + public void actionPerformed(ActionEvent<Action> actionEvent) { + switch(actionEvent.getActionId()) { + case HIDDEN: + stop(); + break; + + case VISIBLE: + start(); + break; + + default: + throw new NotImplementedException("unknown event: " + actionEvent.getActionId()); + } + } + }); + + view.addGCActionListener(new ActionListener<GCCommand>() { + @Override + public void actionPerformed(ActionEvent<GCCommand> actionEvent) { + RequestResponseListener listener = new RequestResponseListener() { + @Override + public void fireComplete(Request request, Response response) { + if (response.getType() == ResponseType.ERROR) { + view.displayWarning(translate.localize( + LocaleResources.ERROR_PERFORMING_GC, + ref.getAgent().getAgentId(), + ref.getIdString())); + } + } + }; + gcRequest.sendGCRequestToAgent(ref, agentDAO, listener); + } + }); + } + + // for testing + VMCollector getCollector() { + return collector; + }; + + Map<String, Payload> getRegions() { + return regions; + } + + private Scale normalizeScale(long min, long max) { + // FIXME: this is very dumb and very inefficient + // needs cleanup + Scale minScale = Scale.getScale(min); + Scale maxScale = Scale.getScale(max); + + Scale[] scales = Scale.values(); + int maxID = 0; + int minID = 0; + for (int i = 0; i < scales.length; i++) { + if (scales[i] == minScale) { + minID = i; + } + if (scales[i] == maxScale) { + maxID = i; + } + } + while (maxID - minID >= 2) { + minID++; + } + return scales[minID]; + } + + private void start() { + timer.start(); + } + + private void stop() { + timer.stop(); + } + + @Override + public String getLocalizedName() { + Translate<LocaleResources> t = LocaleResources.createLocalizer(); + return t.localize(LocaleResources.VM_INFO_TAB_MEMORY); + } + + @Override + public UIComponent getView() { + return (UIComponent) view; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/locale/LocaleResources.java Mon Dec 10 15:05:37 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.vm.memory.client.locale; + +import com.redhat.thermostat.common.locale.Translate; + +public enum LocaleResources { + + VM_INFO_TAB_MEMORY, + ERROR_PERFORMING_GC, + + RESOURCE_MISSING; + + public static final String RESOURCE_BUNDLE = + "com.redhat.thermostat.vm.memory.client.locale.strings"; + + public static Translate<LocaleResources> createLocalizer() { + return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/main/resources/com/redhat/thermostat/vm/memory/client/locale/strings.properties Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,4 @@ +RESOURCE_MISSING = Missing translation! +VM_INFO_TAB_MEMORY = Memory + +ERROR_PERFORMING_GC = Error performing Garbage Collection (agent: {0}, vm: {1}) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/PayloadTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,83 @@ +/* + * 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.vm.memory.client.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; + +import org.junit.Test; + +import com.redhat.thermostat.common.utils.DisplayableValues.Scale; + +public class PayloadTest { + + @Test + public void testClone() { + + StatsModel model = new StatsModel(); + model.setName("fluffModel"); + model.setRange(100); + model.addData(500, 2.0); + model.addData(501, 2.1); + + Payload source = new Payload(); + source.setCapacity(10.0); + source.setName("fluff"); + source.setCapacityUnit(Scale.GiB); + source.setMaxCapacity(100.0); + source.setMaxUsed(5.0); + source.setUsed(3.0); + source.setUsedUnit(Scale.MiB); + source.setModel(model); + source.setTooltip("fluffTooltip"); + + Payload cloned = source.clone(); + assertNotSame(cloned, source); + + assertEquals(source.getName(), cloned.getName()); + assertEquals(source.getCapacity(), cloned.getCapacity(), 0); + assertEquals(source.getCapacityUnit(), cloned.getCapacityUnit()); + assertEquals(source.getMaxCapacity(), cloned.getMaxCapacity(), 0); + assertEquals(source.getMaxUsed(), cloned.getMaxUsed(), 0); + assertEquals(source.getTooltip(), cloned.getTooltip()); + assertEquals(source.getUsed(), cloned.getUsed(), 0); + assertEquals(source.getUsedUnit(), cloned.getUsedUnit()); + assertNotSame(source.getModel(), cloned.getModel()); + + assertEquals(source.getModel().getName(), cloned.getModel().getName()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/RangeModelTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,122 @@ +/* + * 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.vm.memory.client.core; + +import junit.framework.Assert; + +import org.junit.Test; + +public class RangeModelTest { + + @Test + public void testSameRange() { + RangeModel model = new RangeModel(); + + model.setMaximum(10); + model.setMinimum(0); + model.setValue(5); + + model.setMaxNormalized(10); + model.setMinNormalized(0); + + + Assert.assertEquals((int) model.getValue(), model.getValueNormalized()); + } + + @Test + public void testDoubleRange() { + RangeModel model = new RangeModel(); + + model.setMaximum(10); + model.setMinimum(0); + model.setValue(5); + + model.setMaxNormalized(20); + model.setMinNormalized(0); + + + Assert.assertEquals(10, model.getValueNormalized()); + } + + @Test + public void testRanges() { + RangeModel model = new RangeModel(); + + model.setMaximum(10); + model.setMinimum(0); + model.setValue(5); + + model.setMaxNormalized(40); + model.setMinNormalized(0); + + Assert.assertEquals(20, model.getValueNormalized()); + + model.setMaxNormalized(60); + model.setMinNormalized(0); + + Assert.assertEquals(30, model.getValueNormalized()); + + model.setMaxNormalized(200); + model.setMinNormalized(100); + + Assert.assertEquals(150, model.getValueNormalized()); + + model.setMaximum(100); + model.setMinimum(0); + model.setValue(50); + + model.setMaxNormalized(1); + model.setMinNormalized(0); + + Assert.assertEquals(1, model.getValueNormalized()); + + model.setValue(49); + Assert.assertEquals(0, model.getValueNormalized()); + + model.setMaximum(1.0); + model.setMinimum(0.0); + model.setValue(0.5); + + model.setMaxNormalized(100); + model.setMinNormalized(0); + + Assert.assertEquals(50, model.getValueNormalized()); + + model.setValue(0.72); + Assert.assertEquals(72, model.getValueNormalized()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/StatsModelTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,72 @@ +/* + * 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.vm.memory.client.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class StatsModelTest { + + @Test + public void testClone() { + + StatsModel source = new StatsModel(); + source.setName("fluffModel"); + source.setRange(100); + source.addData(500, 2.0); + source.addData(501, 2.1); + + StatsModel cloned = source.clone(); + + assertNotSame(cloned, source); + + assertEquals(source.getName(), cloned.getName()); + + assertNotSame(cloned.getDataSet(), source.getDataSet()); + + for (Object series : cloned.getDataSet().getItems()) { + assertTrue(source.getDataSet().getItems().contains(series)); + } + + assertEquals(cloned.getDataSet().getItemCount(), + source.getDataSet().getItemCount()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/ActivatorTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,97 @@ +/* + * 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.vm.memory.client.core.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import com.redhat.thermostat.client.core.VmInformationService; +import com.redhat.thermostat.common.ApplicationService; +import com.redhat.thermostat.common.dao.AgentInfoDAO; +import com.redhat.thermostat.common.dao.VmMemoryStatDAO; +import com.redhat.thermostat.gc.remote.common.GCRequest; +import com.redhat.thermostat.test.StubBundleContext; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsService; + +public class ActivatorTest { + + @Test + public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception { + StubBundleContext context = new StubBundleContext(); + + Activator activator = new Activator(); + + activator.start(context); + + assertEquals(0, context.getAllServices().size()); + assertNotSame(1, context.getServiceListeners().size()); + + activator.stop(context); + + assertEquals(0, context.getServiceListeners().size()); + } + + @Test + public void verifyActivatorRegistersServices() throws Exception { + StubBundleContext context = new StubBundleContext(); + VmMemoryStatDAO vmMemoryStatDAO = mock(VmMemoryStatDAO.class); + ApplicationService appSvc = mock(ApplicationService.class); + GCRequest gcRequest = mock(GCRequest.class); + AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); + + context.registerService(VmMemoryStatDAO.class, vmMemoryStatDAO, null); + context.registerService(ApplicationService.class, appSvc, null); + context.registerService(GCRequest.class, gcRequest, null); + context.registerService(AgentInfoDAO.class, agentInfoDAO, null); + + Activator activator = new Activator(); + + activator.start(context); + + assertTrue(context.isServiceRegistered(VmInformationService.class.getName(), MemoryStatsService.class)); + + activator.stop(context); + + assertEquals(0, context.getServiceListeners().size()); + assertEquals(4, context.getAllServices().size()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsControllerTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,290 @@ +/* + * 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.vm.memory.client.core.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doNothing; +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.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +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.ApplicationService; +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.common.Timer.SchedulingType; +import com.redhat.thermostat.common.TimerFactory; +import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.dao.AgentInfoDAO; +import com.redhat.thermostat.common.dao.VmMemoryStatDAO; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.gc.remote.common.GCRequest; +import com.redhat.thermostat.gc.remote.common.command.GCCommand; +import com.redhat.thermostat.storage.model.VmMemoryStat; +import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; +import com.redhat.thermostat.storage.model.VmMemoryStat.Space; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsView; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsViewProvider; +import com.redhat.thermostat.vm.memory.client.core.Payload; + +public class MemoryStatsControllerTest { + + private Generation[] generations = new Generation[2]; + + private VmMemoryStatDAO memoryStatDao; + private MemoryStatsView view; + private Timer timer; + + private ActionListener<MemoryStatsView.Action> viewListener; + private ActionListener<GCCommand> gcActionListener; + + private MemoryStatsController controller; + + private Space canary; + + private AgentInfoDAO agentDAO; + private GCRequest gcRequest; + + private VmRef ref; + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Before + public void setUp() { + timer = mock(Timer.class); + ArgumentCaptor<Runnable> actionCaptor = ArgumentCaptor.forClass(Runnable.class); + doNothing().when(timer).setAction(actionCaptor.capture()); + + TimerFactory timerFactory = mock(TimerFactory.class); + when(timerFactory.createTimer()).thenReturn(timer); + ApplicationService appSvc = mock(ApplicationService.class); + when(appSvc.getTimerFactory()).thenReturn(timerFactory); + + List<VmMemoryStat> vmInfo = new ArrayList<>(); + + for (int i = 0; i < 2; i++) { + Generation generation = new Generation(); + generation.setName("fluff" + i); + VmMemoryStat.Space[] spaces = new VmMemoryStat.Space[2 + (1 - i)]; + for (int j = 0; j < 2; j++) { + Space space = new Space(); + space.setName("fluffer" + i + j); + space.setUsed(100); + space.setCapacity(1000); + space.setMaxCapacity(10000); + spaces[j] = space; + } + if (i == 0) { + // special payload because the others have all the same values + canary = new Space(); + canary.setName("canary"); + canary.setUsed(1); + canary.setCapacity(2); + canary.setMaxCapacity(3); + spaces[2] = canary; + } + generation.setSpaces(spaces); + generations[i] = generation; + } + + long timestamp = 1; + int vmID = 1; + for (int i = 0; i < 5; i++) { + VmMemoryStat vmMemory = new VmMemoryStat(timestamp++, vmID, generations); + vmInfo.add(vmMemory); + } + + memoryStatDao = mock(VmMemoryStatDAO.class); + when(memoryStatDao.getLatestVmMemoryStats(any(VmRef.class), anyLong())).thenReturn(vmInfo); + + view = mock(MemoryStatsView.class); + MemoryStatsViewProvider viewProvider = mock(MemoryStatsViewProvider.class); + when(viewProvider.createView()).thenReturn(view); + + ArgumentCaptor<ActionListener> viewArgumentCaptor = + ArgumentCaptor.forClass(ActionListener.class); + doNothing().when(view).addActionListener(viewArgumentCaptor.capture()); + + ArgumentCaptor<ActionListener> gcArgumentCaptor = + ArgumentCaptor.forClass(ActionListener.class); + doNothing().when(view).addGCActionListener(gcArgumentCaptor.capture()); + + ref = mock(VmRef.class); + + agentDAO = mock(AgentInfoDAO.class); + gcRequest = mock(GCRequest.class); + + controller = new MemoryStatsController(appSvc, memoryStatDao, ref, viewProvider, agentDAO, gcRequest); + + viewListener = viewArgumentCaptor.getValue(); + gcActionListener = gcArgumentCaptor.getValue(); + } + + @Test + public void testStartStopTimer() { + viewListener.actionPerformed(new ActionEvent<>(view, MemoryStatsView.Action.VISIBLE)); + + verify(timer).start(); + verify(timer).setSchedulingType(SchedulingType.FIXED_RATE); + + viewListener.actionPerformed(new ActionEvent<>(view, MemoryStatsView.Action.HIDDEN)); + + verify(timer).stop(); + } + + @Test + public void testGCInvoked() { + gcActionListener.actionPerformed(new ActionEvent<>(view, GCCommand.REQUEST_GC)); + verify(gcRequest).sendGCRequestToAgent(eq(ref), eq(agentDAO), isA(RequestResponseListener.class)); + } + + @Test + public void testPayloadContainSpaces() { + MemoryStatsController.VMCollector collettor = controller.getCollector(); + collettor.run(); + + Map<String, Payload> regions = controller.getRegions(); + assertEquals(5, regions.size()); + + assertTrue(regions.containsKey("fluffer00")); + assertTrue(regions.containsKey("fluffer01")); + assertTrue(regions.containsKey("fluffer10")); + assertTrue(regions.containsKey("fluffer11")); + + assertTrue(regions.containsKey("canary")); + } + + @Test + public void testValues() { + MemoryStatsController.VMCollector collettor = controller.getCollector(); + collettor.run(); + + Map<String, Payload> regions = controller.getRegions(); + + Payload payload = regions.get("fluffer00"); + assertEquals("fluffer00", payload.getName()); + assertEquals(10000, payload.getMaxCapacity(), 0); + assertEquals(1000, payload.getCapacity(), 0); + assertEquals(100, payload.getUsed(), 0); + + payload = regions.get("canary"); + assertEquals("canary", payload.getName()); + assertEquals(3, payload.getMaxCapacity(), 0); + assertEquals(2, payload.getCapacity(), 0); + assertEquals(1, payload.getUsed(), 0); + + // the value above all ensure the same scale is used + String tooltip = payload.getName() + ": used: " + payload.getUsed() + " " + + payload.getUsedUnit() + ", capacity: " + + payload.getCapacity() + " " + payload.getUsedUnit() + + ", max capacity: " + payload.getMaxCapacity() + " " + + payload.getUsedUnit(); + + assertEquals(tooltip, payload.getTooltip()); + } + + + @Test + public void testTimerFetchesMemoryDataDeltaOnly() { + ArgumentCaptor<Long> timeStampCaptor = ArgumentCaptor.forClass(Long.class); + + final long DATA_TIMESTAMP = System.currentTimeMillis() + 1000000000; + Space space = new Space(); + space.setCapacity(10); + space.setMaxCapacity(20); + space.setUsed(5); + Generation gen = new Generation(); + gen.setName("foobar"); + gen.setSpaces(new Space[] { space }); + VmMemoryStat stat = new VmMemoryStat(); + stat.setTimeStamp(DATA_TIMESTAMP); + stat.setGenerations(new Generation[] { gen }); + + when(memoryStatDao.getLatestVmMemoryStats(isA(VmRef.class), anyLong())).thenReturn(Arrays.asList(stat)); + + Runnable timerAction = controller.getCollector(); + + timerAction.run(); + timerAction.run(); + + verify(memoryStatDao, times(2)).getLatestVmMemoryStats(isA(VmRef.class), timeStampCaptor.capture()); + + long timeStamp1 = timeStampCaptor.getAllValues().get(0); + assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1), timeStamp1); + + long timeStamp2 = timeStampCaptor.getAllValues().get(1); + assertTimeStampIsAround(DATA_TIMESTAMP, timeStamp2); + } + + @Test + public void testTimerFetchesMemoryDataDeltaOnlyEvenWithNoData() { + ArgumentCaptor<Long> timeStampCaptor = ArgumentCaptor.forClass(Long.class); + + Runnable timerAction = controller.getCollector(); + + timerAction.run(); + timerAction.run(); + + verify(memoryStatDao, times(2)).getLatestVmMemoryStats(isA(VmRef.class), timeStampCaptor.capture()); + + long timeStamp1 = timeStampCaptor.getAllValues().get(0); + assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1), timeStamp1); + + long timeStamp2 = timeStampCaptor.getAllValues().get(1); + assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1), timeStamp2); + } + + private void assertTimeStampIsAround(long expected, long actual) { + assertTrue(actual <= expected + 1000); + assertTrue(actual >= expected - 1000); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/locale/LocaleResourcesTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,53 @@ +/* + * 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.vm.memory.client.locale; + +import com.redhat.thermostat.test.locale.AbstractLocaleResourcesTest; + +public class LocaleResourcesTest extends AbstractLocaleResourcesTest<LocaleResources> { + + @Override + protected Class<LocaleResources> getEnumClass() { + return LocaleResources.class; + } + + @Override + protected String getResourceBundle() { + return LocaleResources.RESOURCE_BUNDLE; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-swing/pom.xml Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>thermostat-vm-memory</artifactId> + <groupId>com.redhat.thermostat</groupId> + <version>0.5.0-SNAPSHOT</version> + </parent> + <artifactId>thermostat-vm-memory-client-swing</artifactId> + <packaging>bundle</packaging> + <name>Thermostat VM Memory Swing Client plugin</name> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Private-Package> + com.redhat.thermostat.vm.memory.client.swing + </Private-Package> + <Bundle-Activator>com.redhat.thermostat.vm.memory.client.swing.Activator</Bundle-Activator> + <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> + <Bundle-SymbolicName>com.redhat.thermostat.vm.memory.client.swing</Bundle-SymbolicName> + <!-- Do not autogenerate uses clauses in Manifests --> + <_nouses>true</_nouses> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easytesting</groupId> + <artifactId>fest-swing</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.java.openjdk.cacio</groupId> + <artifactId>cacio-tta</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.jfree</groupId> + <artifactId>jfreechart</artifactId> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-vm-memory-client-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-client-swing</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-gc-remote-collector-client-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-gc-remote-collector-client-swing</artifactId> + <version>${project.version}</version> + </dependency> + + </dependencies> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-swing/src/main/java/com/redhat/thermostat/vm/memory/client/swing/Activator.java Mon Dec 10 15:05:37 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.vm.memory.client.swing; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsViewProvider; + +public class Activator implements BundleActivator { + + @Override + public void start(BundleContext context) throws Exception { + MemoryStatsViewProvider provider = new SwingMemoryStatsViewProvider(); + // Automatically unregistered on Activator.stop + context.registerService(MemoryStatsViewProvider.class.getName(), provider, null); + } + + @Override + public void stop(BundleContext context) throws Exception { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-swing/src/main/java/com/redhat/thermostat/vm/memory/client/swing/MemoryGraphPanel.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,85 @@ +/* + * 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.vm.memory.client.swing; + +import java.awt.Dimension; +import java.beans.Transient; + +import javax.swing.BoxLayout; +import javax.swing.JPanel; + +import com.redhat.thermostat.vm.memory.client.core.MemoryMeter; +import com.redhat.thermostat.vm.memory.client.core.Payload; + +@SuppressWarnings("serial") +class MemoryGraphPanel extends JPanel { + + private MemoryMeter meter; + + /** + * Create the panel. + */ + public MemoryGraphPanel() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + meter = new MemoryMeter(); + add(meter); + } + + public void setMemoryGraphProperties(Payload region) { + + meter.getPrimaryModel().setMinimum(0); + meter.getPrimaryModel().setMaximum(region.getMaxUsed()); + meter.getPrimaryModel().setValue(region.getUsed()); + + meter.getSecondaryModel().setMinimum(0); + meter.getSecondaryModel().setMaximum(region.getMaxCapacity()); + meter.getSecondaryModel().setValue(region.getCapacity()); + + meter.setToolTipText(region.getTooltip()); + + meter.setPrimaryScaleUnit(region.getUsedUnit().toString()); + meter.setSecondayScaleUnit(region.getCapacityUnit().toString()); + + meter.setStats(region.getModel()); + } + + @Override + @Transient + public Dimension getPreferredSize() { + return meter.getPreferredSize(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-swing/src/main/java/com/redhat/thermostat/vm/memory/client/swing/MemoryStatsViewImpl.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,174 @@ +/* + * 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.vm.memory.client.swing; + +import java.awt.Component; +import java.awt.Dimension; +import java.beans.Transient; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.client.core.views.BasicView; +import com.redhat.thermostat.client.swing.SwingComponent; +import com.redhat.thermostat.client.swing.components.HeaderPanel; +import com.redhat.thermostat.client.ui.ComponentVisibleListener; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.gc.remote.client.common.RequestGCAction; +import com.redhat.thermostat.gc.remote.client.swing.ToolbarGCButton; +import com.redhat.thermostat.gc.remote.common.command.GCCommand; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsView; +import com.redhat.thermostat.vm.memory.client.core.Payload; + +public class MemoryStatsViewImpl extends MemoryStatsView implements SwingComponent { + + private static final long REPAINT_DELAY = 500; + private long lastRepaint; + + private HeaderPanel visiblePanel; + private JPanel realPanel; + + private final Map<String, MemoryGraphPanel> regions; + + private RequestGCAction toobarButtonAction; + + private Dimension preferredSize; + + public MemoryStatsViewImpl() { + super(); + visiblePanel = new HeaderPanel(); + regions = new HashMap<>(); + + preferredSize = new Dimension(0, 0); + + visiblePanel.setHeader("Memory Regions"); + + visiblePanel.addHierarchyListener(new ComponentVisibleListener() { + @Override + public void componentShown(Component component) { + notifier.fireAction(Action.VISIBLE); + } + + @Override + public void componentHidden(Component component) { + notifier.fireAction(Action.HIDDEN); + } + }); + + realPanel = new JPanel(); + realPanel.setLayout(new BoxLayout(realPanel, BoxLayout.Y_AXIS)); + visiblePanel.setContent(realPanel); + + toobarButtonAction = new RequestGCAction(); + visiblePanel.addToolBarButton(new ToolbarGCButton(toobarButtonAction)); + } + + @Transient + public Dimension getPreferredSize() { + return new Dimension(preferredSize); + } + + @Override + public void updateRegion(final Payload region) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + MemoryGraphPanel memoryGraphPanel = regions.get(region.getName()); + memoryGraphPanel.setMemoryGraphProperties(region); + } + }); + } + + @Override + public void addGCActionListener(ActionListener<GCCommand> listener) { + toobarButtonAction.addActionListener(listener); + } + + @Override + public void addRegion(final Payload region) { + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + MemoryGraphPanel memoryGraphPanel = new MemoryGraphPanel(); + + realPanel.add(memoryGraphPanel); + realPanel.add(Box.createRigidArea(new Dimension(5,5))); + regions.put(region.getName(), memoryGraphPanel); + + // components are stacked up vertically in this panel + Dimension memoryGraphPanelMinSize = memoryGraphPanel.getMinimumSize(); + preferredSize.height += memoryGraphPanelMinSize.height + 5; + if (preferredSize.width < (memoryGraphPanelMinSize.width + 5)) { + preferredSize.width = memoryGraphPanelMinSize.width + 5; + } + + updateRegion(region); + realPanel.revalidate(); + } + }); + } + + @Override + public void displayWarning(String string) { + JOptionPane.showMessageDialog(visiblePanel, string, "Warning", JOptionPane.WARNING_MESSAGE); + } + + @Override + public Component getUiComponent() { + return visiblePanel; + } + + @Override + public void requestRepaint() { + // really only repaint every REPAINT_DELAY milliseconds + long now = System.currentTimeMillis(); + if (now - lastRepaint > REPAINT_DELAY) { + visiblePanel.repaint(); + lastRepaint = System.currentTimeMillis(); + } + } + + public BasicView getView() { + return this; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-swing/src/main/java/com/redhat/thermostat/vm/memory/client/swing/SwingMemoryStatsViewProvider.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,49 @@ +/* + * 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.vm.memory.client.swing; + +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsView; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsViewProvider; + +public class SwingMemoryStatsViewProvider implements MemoryStatsViewProvider { + + @Override + public MemoryStatsView createView() { + return new MemoryStatsViewImpl(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-swing/src/test/java/com/redhat/thermostat/vm/memory/client/swing/ActivatorTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,59 @@ +/* + * 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.vm.memory.client.swing; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.redhat.thermostat.test.StubBundleContext; +import com.redhat.thermostat.vm.memory.client.core.MemoryStatsViewProvider; +import com.redhat.thermostat.vm.memory.client.swing.Activator; +import com.redhat.thermostat.vm.memory.client.swing.SwingMemoryStatsViewProvider; + +public class ActivatorTest { + + @Test + public void verifyStartRegistersViewProvider() throws Exception { + StubBundleContext ctx = new StubBundleContext(); + Activator activator = new Activator(); + activator.start(ctx); + assertTrue(ctx.isServiceRegistered(MemoryStatsViewProvider.class.getName(), SwingMemoryStatsViewProvider.class)); + assertEquals(1, ctx.getAllServices().size()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/pom.xml Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat</artifactId> + <version>0.5.0-SNAPSHOT</version> + </parent> + + <artifactId>thermostat-vm-memory</artifactId> + <packaging>pom</packaging> + + <name>Thermostat VM Memory plugin</name> + + <modules> + <module>client-core</module> + <module>client-swing</module> + </modules> + +</project>
--- a/vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/VmOverviewPanel.java Mon Dec 10 14:30:36 2012 +0100 +++ b/vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/VmOverviewPanel.java Mon Dec 10 15:05:37 2012 +0100 @@ -37,10 +37,16 @@ package com.redhat.thermostat.vm.overview.client.swing; import java.awt.Component; +import java.awt.Point; +import java.awt.Rectangle; import java.util.ArrayList; import java.util.List; import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.client.swing.components.Components; @@ -58,6 +64,7 @@ private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); private HeaderPanel visiblePanel; + private JScrollPane container; private final ChangeableText pid = new ChangeableText(""); private final ChangeableText startTimeStamp = new ChangeableText(""); @@ -69,6 +76,7 @@ private final ChangeableText vmNameAndVersion = new ChangeableText(""); private final ChangeableText vmArguments = new ChangeableText(""); + public VmOverviewPanel() { super(); initializePanel(); @@ -181,6 +189,9 @@ SimpleTable simpleTable = new SimpleTable(); JPanel table = simpleTable.createTable(allSections); table.setBorder(Components.smallBorder()); - visiblePanel.setContent(table); + + container = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + visiblePanel.setContent(container); } }
--- a/web/client/pom.xml Mon Dec 10 14:30:36 2012 +0100 +++ b/web/client/pom.xml Mon Dec 10 15:05:37 2012 +0100 @@ -122,10 +122,10 @@ <instructions> <Bundle-SymbolicName>com.redhat.thermostat.web.client</Bundle-SymbolicName> <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> - <Bundle-Activator>com.redhat.thermostat.web.client.Activator</Bundle-Activator> - <Export-Package> - com.redhat.thermostat.web.client - </Export-Package> + <Bundle-Activator>com.redhat.thermostat.web.client.internal.Activator</Bundle-Activator> + <Private-Package> + com.redhat.thermostat.web.client.internal + </Private-Package> <!-- Do not autogenerate uses clauses in Manifests --> <_nouses>true</_nouses> </instructions>
--- a/web/client/src/main/java/com/redhat/thermostat/web/client/Activator.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +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.web.client; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; - -import com.redhat.thermostat.storage.core.Storage; -import com.redhat.thermostat.storage.core.StorageProvider; - -public class Activator implements BundleActivator { - - private ServiceRegistration reg; - - @Override - public void start(BundleContext context) throws Exception { - WebStorageProvider storage = new WebStorageProvider(); - this.reg = context.registerService(StorageProvider.class.getName(), storage, null); - } - - @Override - public void stop(BundleContext context) throws Exception { - reg.unregister(); - } - -}
--- a/web/client/src/main/java/com/redhat/thermostat/web/client/WebCursor.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +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.web.client; - -import com.redhat.thermostat.storage.core.Cursor; -import com.redhat.thermostat.storage.model.Pojo; - -class WebCursor<T extends Pojo> implements Cursor<T> { - - private T[] data; - private int index; - - WebCursor(T[] data) { - this.data = data; - index = 0; - } - - @Override - public boolean hasNext() { - return index < data.length; - } - - @Override - public T next() { - T result = data[index]; - index++; - return result; - } - -}
--- a/web/client/src/main/java/com/redhat/thermostat/web/client/WebStorage.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,531 +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.web.client; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.lang.reflect.Array; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.apache.commons.codec.binary.Base64; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.StatusLine; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.Credentials; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.entity.mime.MultipartEntity; -import org.apache.http.entity.mime.content.InputStreamBody; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.redhat.thermostat.storage.core.AuthToken; -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.Connection; -import com.redhat.thermostat.storage.core.Cursor; -import com.redhat.thermostat.storage.core.Query; -import com.redhat.thermostat.storage.core.Remove; -import com.redhat.thermostat.storage.core.SecureStorage; -import com.redhat.thermostat.storage.core.Storage; -import com.redhat.thermostat.storage.core.StorageException; -import com.redhat.thermostat.storage.core.Update; -import com.redhat.thermostat.storage.model.AgentIdPojo; -import com.redhat.thermostat.storage.model.Pojo; -import com.redhat.thermostat.web.common.ThermostatGSONConverter; -import com.redhat.thermostat.web.common.WebInsert; -import com.redhat.thermostat.web.common.WebQuery; -import com.redhat.thermostat.web.common.WebRemove; -import com.redhat.thermostat.web.common.WebUpdate; - -public class WebStorage implements Storage, SecureStorage { - - private static class CloseableHttpEntity implements Closeable, HttpEntity { - - private HttpEntity entity; - - CloseableHttpEntity(HttpEntity entity) { - this.entity = entity; - } - - @Override - public void consumeContent() throws IOException { - entity.consumeContent(); - } - - @Override - public InputStream getContent() throws IOException, IllegalStateException { - return entity.getContent(); - } - - @Override - public Header getContentEncoding() { - return entity.getContentEncoding(); - } - - @Override - public long getContentLength() { - return entity.getContentLength(); - } - - @Override - public Header getContentType() { - return entity.getContentType(); - } - - @Override - public boolean isChunked() { - return entity.isChunked(); - } - - @Override - public boolean isRepeatable() { - return entity.isRepeatable(); - } - - @Override - public boolean isStreaming() { - return entity.isStreaming(); - } - - @Override - public void writeTo(OutputStream out) throws IOException { - entity.writeTo(out); - } - - @Override - public void close() { - try { - EntityUtils.consume(entity); - } catch (IOException ex) { - throw new StorageException(ex); - } - } - - } - - private final class WebConnection extends Connection { - WebConnection() { - connected = true; - } - @Override - public void disconnect() { - connected = false; - fireChanged(ConnectionStatus.DISCONNECTED); - } - - @Override - public void connect() { - try { - initAuthentication(httpClient); - ping(); - connected = true; - fireChanged(ConnectionStatus.CONNECTED); - } catch (Exception ex) { - fireChanged(ConnectionStatus.FAILED_TO_CONNECT); - } - } - - @Override - public void setUrl(String url) { - super.setUrl(url); - endpoint = url; - } - - @Override - public String getUrl() { - return endpoint; - } - } - - private static class WebDataStream extends InputStream { - - private CloseableHttpEntity entity; - private InputStream content; - - WebDataStream(CloseableHttpEntity entity) { - this.entity = entity; - try { - content = entity.getContent(); - } catch (IllegalStateException | IOException e) { - throw new StorageException(e); - } - } - - @Override - public void close() throws IOException { - content.close(); - entity.close(); - } - - @Override - public int read() throws IOException { - return content.read(); - } - - @Override - public int available() throws IOException { - return content.available(); - } - - @Override - public void mark(int readlimit) { - content.mark(readlimit); - } - - @Override - public boolean markSupported() { - return content.markSupported(); - } - - @Override - public int read(byte[] b) throws IOException { - return content.read(b); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - return content.read(b, off, len); - } - - @Override - public void reset() throws IOException { - content.reset(); - } - - @Override - public long skip(long n) throws IOException { - return content.skip(n); - } - - } - - private String endpoint; - private UUID agentId; - - private Map<Category, Integer> categoryIds; - private Gson gson; - private DefaultHttpClient httpClient; - private String username; - private String password; - private SecureRandom random; - - public WebStorage() { - categoryIds = new HashMap<>(); - gson = new GsonBuilder().registerTypeHierarchyAdapter(Pojo.class, new ThermostatGSONConverter()).create(); - ClientConnectionManager connManager = new ThreadSafeClientConnManager(); - DefaultHttpClient client = new DefaultHttpClient(connManager); - httpClient = client; - random = new SecureRandom(); - } - - private void initAuthentication(DefaultHttpClient client) throws MalformedURLException { - if (username != null && password != null) { - URL endpointURL = new URL(endpoint); - // TODO: Maybe also limit to realm like 'Thermostat Realm' or such? - AuthScope scope = new AuthScope(endpointURL.getHost(), endpointURL.getPort()); - Credentials creds = new UsernamePasswordCredentials(username, password); - client.getCredentialsProvider().setCredentials(scope, creds); - } - } - - private void ping() throws StorageException { - post(endpoint + "/ping", (HttpEntity) null).close(); - } - - private CloseableHttpEntity post(String url, List<NameValuePair> formparams) throws StorageException { - try { - return postImpl(url, formparams); - } catch (IOException ex) { - throw new StorageException(ex); - } - } - - private CloseableHttpEntity postImpl(String url, List<NameValuePair> formparams) throws IOException { - HttpEntity entity; - if (formparams != null) { - entity = new UrlEncodedFormEntity(formparams, "UTF-8"); - } else { - entity = null; - } - return postImpl(url, entity); - } - - - private CloseableHttpEntity post(String url, HttpEntity entity) throws StorageException { - try { - return postImpl(url, entity); - } catch (IOException ex) { - throw new StorageException(ex); - } - } - - private CloseableHttpEntity postImpl(String url, HttpEntity entity) throws IOException { - HttpPost httpPost = new HttpPost(url); - if (entity != null) { - httpPost.setEntity(entity); - } - HttpResponse response = httpClient.execute(httpPost); - StatusLine status = response.getStatusLine(); - if (status.getStatusCode() != 200) { - throw new IOException("Server returned status: " + status); - } - - return new CloseableHttpEntity(response.getEntity()); - } - - private static InputStream getContent(HttpEntity entity) { - try { - return entity.getContent(); - } catch (IOException ex) { - throw new StorageException(ex); - } - } - - private static Reader getContentAsReader(HttpEntity entity) { - InputStream in = getContent(entity); - return new InputStreamReader(in); - } - - @Override - public void registerCategory(Category category) throws StorageException { - NameValuePair nameParam = new BasicNameValuePair("name", category.getName()); - NameValuePair categoryParam = new BasicNameValuePair("category", gson.toJson(category)); - List<NameValuePair> formparams = Arrays.asList(nameParam, categoryParam); - try (CloseableHttpEntity entity = post(endpoint + "/register-category", formparams)) { - Reader reader = getContentAsReader(entity); - Integer id = gson.fromJson(reader, Integer.class); - categoryIds.put(category, id); - } - } - - @Override - public Query createQuery() { - return new WebQuery(categoryIds); - } - - @Override - public Remove createRemove() { - return new WebRemove(categoryIds); - } - - @Override - public WebUpdate createUpdate() { - return new WebUpdate(categoryIds); - } - - @Override - public <T extends Pojo> Cursor<T> findAllPojos(Query query, Class<T> resultClass) throws StorageException { - ((WebQuery) query).setResultClassName(resultClass.getName()); - NameValuePair queryParam = new BasicNameValuePair("query", gson.toJson(query)); - List<NameValuePair> formparams = Arrays.asList(queryParam); - try (CloseableHttpEntity entity = post(endpoint + "/find-all", formparams)) { - Reader reader = getContentAsReader(entity); - T[] result = (T[]) gson.fromJson(reader, Array.newInstance(resultClass, 0).getClass()); - return new WebCursor<T>(result); - } - } - - @Override - public <T extends Pojo> T findPojo(Query query, Class<T> resultClass) throws StorageException { - ((WebQuery) query).setResultClassName(resultClass.getName()); - NameValuePair queryParam = new BasicNameValuePair("query", gson.toJson(query)); - List<NameValuePair> formparams = Arrays.asList(queryParam); - try (CloseableHttpEntity entity = post(endpoint + "/find-pojo", formparams)) { - Reader reader = getContentAsReader(entity); - T result = gson.fromJson(reader, resultClass); - return result; - } - } - - @Override - public String getAgentId() { - return agentId.toString(); - } - - @Override - public Connection getConnection() { - return new WebConnection(); - } - - @Override - public long getCount(Category category) throws StorageException { - NameValuePair categoryParam = new BasicNameValuePair("category", gson.toJson(categoryIds.get(category))); - List<NameValuePair> formparams = Arrays.asList(categoryParam); - try (CloseableHttpEntity entity = post(endpoint + "/get-count", formparams)) { - Reader reader = getContentAsReader(entity); - long result = gson.fromJson(reader, Long.class); - return result; - } - } - - @Override - public InputStream loadFile(String name) throws StorageException { - NameValuePair fileParam = new BasicNameValuePair("file", name); - List<NameValuePair> formparams = Arrays.asList(fileParam); - CloseableHttpEntity entity = post(endpoint + "/load-file", formparams); - return new WebDataStream(entity); - } - - @Override - public void purge() throws StorageException { - post(endpoint + "/purge", (HttpEntity) null).close(); - } - - @Override - public void putPojo(Category category, boolean replace, AgentIdPojo pojo) throws StorageException { - // TODO: This logic should probably be moved elsewhere. I.e. out of the Storage API. - if (pojo.getAgentId() == null) { - pojo.setAgentId(getAgentId()); - } - - int categoryId = categoryIds.get(category); - WebInsert insert = new WebInsert(categoryId, replace, pojo.getClass().getName()); - NameValuePair insertParam = new BasicNameValuePair("insert", gson.toJson(insert)); - NameValuePair pojoParam = new BasicNameValuePair("pojo", gson.toJson(pojo)); - List<NameValuePair> formparams = Arrays.asList(insertParam, pojoParam); - post(endpoint + "/put-pojo", formparams).close(); - } - - @Override - public void removePojo(Remove remove) throws StorageException { - NameValuePair removeParam = new BasicNameValuePair("remove", gson.toJson(remove)); - List<NameValuePair> formparams = Arrays.asList(removeParam); - post(endpoint + "/remove-pojo", formparams).close(); - } - - @Override - public void saveFile(String name, InputStream in) throws StorageException { - InputStreamBody body = new InputStreamBody(in, name); - MultipartEntity mpEntity = new MultipartEntity(); - mpEntity.addPart("file", body); - post(endpoint + "/save-file", mpEntity).close(); - } - - @Override - public void setAgentId(UUID agentId) { - this.agentId = agentId; - } - - @Override - public void updatePojo(Update update) throws StorageException { - WebUpdate webUp = (WebUpdate) update; - List<WebUpdate.UpdateValue> updateValues = webUp.getUpdates(); - List<Object> values = new ArrayList<>(updateValues.size()); - for (WebUpdate.UpdateValue updateValue : updateValues) { - values.add(updateValue.getValue()); - } - - NameValuePair updateParam = new BasicNameValuePair("update", gson.toJson(update)); - NameValuePair valuesParam = new BasicNameValuePair("values", gson.toJson(values)); - List<NameValuePair> formparams = Arrays.asList(updateParam, valuesParam); - post(endpoint + "/update-pojo", formparams).close(); - } - - public void setEndpoint(String endpoint) { - this.endpoint = endpoint; - } - - public void setAuthConfig(String username, String password) { - this.username = username; - this.password = password; - } - - @Override - public AuthToken generateToken() throws StorageException { - byte[] token = new byte[256]; - random.nextBytes(token); - NameValuePair clientTokenParam = new BasicNameValuePair("client-token", Base64.encodeBase64String(token)); - List<NameValuePair> formparams = Arrays.asList(clientTokenParam); - try (CloseableHttpEntity entity = post(endpoint + "/generate-token", formparams)) { - byte[] authToken = EntityUtils.toByteArray(entity); - return new AuthToken(authToken, token); - } catch (IOException ex) { - throw new StorageException(ex); - } - } - - @Override - public boolean verifyToken(AuthToken authToken) { - byte[] clientToken = authToken.getClientToken(); - byte[] token = authToken.getToken(); - NameValuePair clientTokenParam = new BasicNameValuePair("client-token", Base64.encodeBase64String(clientToken)); - NameValuePair tokenParam = new BasicNameValuePair("token", Base64.encodeBase64String(token)); - List<NameValuePair> formparams = Arrays.asList(clientTokenParam, tokenParam); - HttpResponse response = null; - try { - HttpEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8"); - HttpPost httpPost = new HttpPost(endpoint + "/verify-token"); - httpPost.setEntity(entity); - response = httpClient.execute(httpPost); - StatusLine status = response.getStatusLine(); - return status.getStatusCode() == 200; - } catch (IOException ex) { - throw new StorageException(ex); - } finally { - if (response != null) { - try { - EntityUtils.consume(response.getEntity()); - } catch (IOException ex) { - throw new StorageException(ex); - } - } - } - } - -}
--- a/web/client/src/main/java/com/redhat/thermostat/web/client/WebStorageProvider.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -package com.redhat.thermostat.web.client; - -import com.redhat.thermostat.storage.config.AuthenticationConfiguration; -import com.redhat.thermostat.storage.config.StartupConfiguration; -import com.redhat.thermostat.storage.core.Storage; -import com.redhat.thermostat.storage.core.StorageProvider; - -public class WebStorageProvider implements StorageProvider { - - private StartupConfiguration config; - - @Override - public Storage createStorage() { - WebStorage storage = new WebStorage(); - storage.setEndpoint(config.getDBConnectionString()); - if (config instanceof AuthenticationConfiguration) { - AuthenticationConfiguration authConf = (AuthenticationConfiguration) config; - storage.setAuthConfig(authConf.getUsername(), authConf.getPassword()); - } - return storage; - } - - @Override - public void setConfig(StartupConfiguration config) { - this.config = config; - } - - @Override - public boolean canHandleProtocol() { - // use http since this might be https at some point - return config.getDBConnectionString().startsWith("http"); - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/Activator.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,61 @@ +/* + * 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.web.client.internal; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import com.redhat.thermostat.storage.core.StorageProvider; + +public class Activator implements BundleActivator { + + private ServiceRegistration reg; + + @Override + public void start(BundleContext context) throws Exception { + WebStorageProvider storage = new WebStorageProvider(); + this.reg = context.registerService(StorageProvider.class.getName(), storage, null); + } + + @Override + public void stop(BundleContext context) throws Exception { + reg.unregister(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/CustomX509TrustManager.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,238 @@ +/* + * 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.web.client.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import com.redhat.thermostat.common.utils.LoggingUtils; + +/** + * Custom X509TrustManager which first attempts to verify peer certificates + * using Java's default trust manager. If that fails it uses thermostat's + * keystore file (if any) and uses it for another attempt to validate the + * server certificate. If that fails as well, the connection is not allowed. + * + */ +class CustomX509TrustManager implements X509TrustManager { + + Logger logger = LoggingUtils.getLogger(CustomX509TrustManager.class); + + /* + * The default X509TrustManager returned by SunX509. We'll delegate + * decisions to it, and fall back to the logic in this class if the default + * X509TrustManager doesn't trust it. + */ + private X509TrustManager defaultX509TrustManager; + private X509TrustManager ourX509TrustManager; + + // For testing + CustomX509TrustManager(X509TrustManager defaultTrustManager, + X509TrustManager ourTrustManager) { + this.defaultX509TrustManager = defaultTrustManager; + this.ourX509TrustManager = ourTrustManager; + } + + // For testing + CustomX509TrustManager(X509TrustManager defaultTrustManager, + File keyStoreFile, String keyStorePassword) { + this.defaultX509TrustManager = defaultTrustManager; + this.ourX509TrustManager = getOurTrustManager(keyStoreFile, keyStorePassword); + } + + // For testing + CustomX509TrustManager(File keyStoreFile, String keyStorePassword) { + this.defaultX509TrustManager = getDefaultTrustManager(); + this.ourX509TrustManager = getOurTrustManager(keyStoreFile, keyStorePassword); + } + + /* + * Main constructor. Others are used for testing. + */ + CustomX509TrustManager() { + this(SSLKeystoreConfiguration.getKeystoreFile(), SSLKeystoreConfiguration.getKeyStorePassword()); + } + + private X509TrustManager getDefaultTrustManager() { + try { + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + "SunX509", "SunJSSE"); + // Passing a null-KeyStore in order to get default Java behaviour. + // See: + // http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager + tmf.init((KeyStore) null); + + TrustManager tms[] = tmf.getTrustManagers(); + + /* + * Iterate over the returned trustmanagers, look for an instance of + * X509TrustManager. If found, use that as our "default" trust + * manager. + */ + for (int i = 0; i < tms.length; i++) { + if (tms[i] instanceof X509TrustManager) { + logger.log(Level.FINE, "Using system trust manager."); + return (X509TrustManager) tms[i]; + } + } + } catch (NoSuchAlgorithmException | NoSuchProviderException | KeyStoreException e) { + logger.log(Level.WARNING, "Could not retrieve system trust manager", e); + } + return null; + } + + /* + * If thermostat trust store file exists and a X509TrustManager can be + * retrieved from said trust store it returns this X509TrustManager. Returns + * null if an exception is thrown along the way, the keystore file does not + * exist or no X509TrustManager was found in the backing trust store. + */ + private X509TrustManager getOurTrustManager(File trustStoreFile, + String keyStorePassword) { + KeyStore trustStore = null; + if (trustStoreFile != null && trustStoreFile.exists()) { + try { + trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + try (InputStream is = new FileInputStream(trustStoreFile)) { + trustStore.load(is, keyStorePassword.toCharArray()); + } catch (IOException | CertificateException + | NoSuchAlgorithmException e) { + logger.log(Level.WARNING, + "Could not load Thermostat trust manager"); + return null; + } + TrustManagerFactory tmf = null; + tmf = TrustManagerFactory.getInstance("SunX509", "SunJSSE"); + tmf.init(trustStore); + + for (TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + logger.log(Level.FINE, + "Using Thermostat trust manager."); + return (X509TrustManager) tm; + } + } + } catch (NoSuchAlgorithmException | NoSuchProviderException + | KeyStoreException e) { + logger.log(Level.WARNING, + "Could not load Thermostat trust manager"); + return null; + } + } + logger.log(Level.FINE, "No Thermostat trust manager found"); + return null; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + // no-op: we don't support client authentication + logger.log(Level.INFO, "Checking client authentication: Allowing all client certificates!"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + logger.log(Level.FINE, "Checking server certificate"); + if (defaultX509TrustManager != null) { + try { + defaultX509TrustManager.checkServerTrusted(chain, authType); + logger.log(Level.FINE, "Server certificate check passed using system trust manager"); + return; + } catch (CertificateException e) { + if (ourX509TrustManager != null) { + // try our trust manager instead + ourX509TrustManager.checkServerTrusted(chain, authType); + logger.log(Level.FINE, "Server certificate check passed using Thermostat trust manager"); + return; + } else { + // just rethrow + logger.log(Level.WARNING, "Server certificate check FAILED!", e); + throw e; + } + } + } else if (ourX509TrustManager != null) { + ourX509TrustManager.checkServerTrusted(chain, authType); + logger.log(Level.FINE, "Server certificate check passed using Thermostat trust manager"); + return; + } + logger.log(Level.SEVERE, "Server certificate could not be checked. No trust managers found. Stopping now."); + // Default to not trusting this cert + throw new CertificateException("Certificate verification failed!"); + } + + /* + * Union of CA's trusted by default trust manager and the thermostat trust + * manager (if any). + * + * (non-Javadoc) + * + * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() + */ + @Override + public X509Certificate[] getAcceptedIssuers() { + Set<X509Certificate> trustedSet = new HashSet<>(); + if (defaultX509TrustManager != null) { + trustedSet.addAll(Arrays.asList(defaultX509TrustManager + .getAcceptedIssuers())); + } + if (ourX509TrustManager != null) { + trustedSet.addAll(Arrays.asList(ourX509TrustManager + .getAcceptedIssuers())); + } + X509Certificate[] certs = trustedSet.toArray(new X509Certificate[0]); + return certs; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/SSLKeystoreConfiguration.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,117 @@ +/* + * 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.web.client.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +import com.redhat.thermostat.common.config.ConfigUtils; +import com.redhat.thermostat.common.config.InvalidConfigurationException; + +public class SSLKeystoreConfiguration { + + private static Properties clientProps = null; + private static final String KEYSTORE_FILE_KEY = "KEYSTORE_FILE"; + private static final String KEYSTORE_FILE_PWD_KEY = "KEYSTORE_PASSWORD"; + + /** + * + * @return The keystore file as specified in + * $THERMOSTAT_HOME/client.properties if any. null otherwise. + */ + public static File getKeystoreFile() { + try { + loadClientProperties(); + } catch (InvalidConfigurationException e) { + // Thermostat home not set? Should have failed earlier. Do something + // reasonable. + return null; + } + String path = clientProps.getProperty(KEYSTORE_FILE_KEY); + if (path != null) { + File file = new File(path); + return file; + } + return null; + } + + /** + * + * @return The keystore file as specified in + * $THERMOSTAT_HOME/client.properties if any. The empty string + * otherwise. + * @throws InvalidConfigurationException + */ + public static String getKeyStorePassword() { + try { + loadClientProperties(); + } catch (InvalidConfigurationException e) { + // Thermostat home not set? Do something reasonable + return ""; + } + String pwd = clientProps.getProperty(KEYSTORE_FILE_PWD_KEY); + if (pwd == null) { + return ""; + } else { + return pwd; + } + } + + // testing hook + static void initClientProperties(File clientPropertiesFile) { + clientProps = new Properties(); + try { + clientProps.load(new FileInputStream(clientPropertiesFile)); + } catch (IOException | IllegalArgumentException e) { + // Could not load client properties file. This is fine as it's + // an optional config after all. + } + } + + private static void loadClientProperties() + throws InvalidConfigurationException { + if (clientProps == null) { + File thermostatEtcDir = new File(ConfigUtils.getThermostatHome(), + "etc"); + File clientPropertiesFile = new File(thermostatEtcDir, + "client.properties"); + initClientProperties(clientPropertiesFile); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebCursor.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,65 @@ +/* + * 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.web.client.internal; + +import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.model.Pojo; + +class WebCursor<T extends Pojo> implements Cursor<T> { + + private T[] data; + private int index; + + WebCursor(T[] data) { + this.data = data; + index = 0; + } + + @Override + public boolean hasNext() { + return index < data.length; + } + + @Override + public T next() { + T result = data[index]; + index++; + return result; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,604 @@ +/* + * 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.web.client.internal; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.lang.reflect.Array; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.commons.codec.binary.Base64; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.StatusLine; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.entity.mime.MultipartEntity; +import org.apache.http.entity.mime.content.InputStreamBody; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.core.AuthToken; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.storage.config.StartupConfiguration; +import com.redhat.thermostat.storage.core.Category; +import com.redhat.thermostat.storage.core.Connection; +import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.core.Query; +import com.redhat.thermostat.storage.core.Remove; +import com.redhat.thermostat.storage.core.SecureStorage; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.core.StorageException; +import com.redhat.thermostat.storage.core.Update; +import com.redhat.thermostat.storage.model.AgentIdPojo; +import com.redhat.thermostat.storage.model.Pojo; +import com.redhat.thermostat.web.common.ThermostatGSONConverter; +import com.redhat.thermostat.web.common.WebInsert; +import com.redhat.thermostat.web.common.WebQuery; +import com.redhat.thermostat.web.common.WebRemove; +import com.redhat.thermostat.web.common.WebUpdate; + +public class WebStorage implements Storage, SecureStorage { + + private static final String HTTPS_PREFIX = "https"; + final Logger logger = LoggingUtils.getLogger(WebStorage.class); + + private static class CloseableHttpEntity implements Closeable, HttpEntity { + + private HttpEntity entity; + + CloseableHttpEntity(HttpEntity entity) { + this.entity = entity; + } + + @Override + public void consumeContent() throws IOException { + EntityUtils.consume(entity); + } + + @Override + public InputStream getContent() throws IOException, + IllegalStateException { + return entity.getContent(); + } + + @Override + public Header getContentEncoding() { + return entity.getContentEncoding(); + } + + @Override + public long getContentLength() { + return entity.getContentLength(); + } + + @Override + public Header getContentType() { + return entity.getContentType(); + } + + @Override + public boolean isChunked() { + return entity.isChunked(); + } + + @Override + public boolean isRepeatable() { + return entity.isRepeatable(); + } + + @Override + public boolean isStreaming() { + return entity.isStreaming(); + } + + @Override + public void writeTo(OutputStream out) throws IOException { + entity.writeTo(out); + } + + @Override + public void close() { + try { + EntityUtils.consume(entity); + } catch (IOException ex) { + throw new StorageException(ex); + } + } + + } + + private final class WebConnection extends Connection { + WebConnection() { + connected = true; + } + + @Override + public void disconnect() { + connected = false; + fireChanged(ConnectionStatus.DISCONNECTED); + } + + @Override + public void connect() { + try { + initAuthentication(httpClient); + ping(); + connected = true; + logger.fine("Connected to storage"); + fireChanged(ConnectionStatus.CONNECTED); + } catch (Exception ex) { + logger.log(Level.WARNING, "Could not connect to storage!", ex); + fireChanged(ConnectionStatus.FAILED_TO_CONNECT); + } + } + + @Override + public void setUrl(String url) { + super.setUrl(url); + endpoint = url; + } + + @Override + public String getUrl() { + return endpoint; + } + } + + private static class WebDataStream extends InputStream { + + private CloseableHttpEntity entity; + private InputStream content; + + WebDataStream(CloseableHttpEntity entity) { + this.entity = entity; + try { + content = entity.getContent(); + } catch (IllegalStateException | IOException e) { + throw new StorageException(e); + } + } + + @Override + public void close() throws IOException { + content.close(); + entity.close(); + } + + @Override + public int read() throws IOException { + return content.read(); + } + + @Override + public int available() throws IOException { + return content.available(); + } + + @Override + public void mark(int readlimit) { + content.mark(readlimit); + } + + @Override + public boolean markSupported() { + return content.markSupported(); + } + + @Override + public int read(byte[] b) throws IOException { + return content.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return content.read(b, off, len); + } + + @Override + public void reset() throws IOException { + content.reset(); + } + + @Override + public long skip(long n) throws IOException { + return content.skip(n); + } + + } + + private String endpoint; + private UUID agentId; + + private Map<Category, Integer> categoryIds; + private Gson gson; + // package private for testing + DefaultHttpClient httpClient; + private String username; + private String password; + private SecureRandom random; + + public WebStorage(StartupConfiguration config) throws StorageException { + categoryIds = new HashMap<>(); + gson = new GsonBuilder().registerTypeHierarchyAdapter(Pojo.class, + new ThermostatGSONConverter()).create(); + ClientConnectionManager connManager = new ThreadSafeClientConnManager(); + DefaultHttpClient client = new DefaultHttpClient(connManager); + httpClient = client; + random = new SecureRandom(); + + // setup SSL if necessary + if (config.getDBConnectionString().startsWith(HTTPS_PREFIX)) { + registerSSLScheme(connManager); + } + } + + private void registerSSLScheme(ClientConnectionManager conManager) + throws StorageException { + try { + SSLContext sc = SSLContext.getInstance("TLS"); + X509TrustManager ourTm; + try { + ourTm = new CustomX509TrustManager(); + } catch (Exception e) { + throw new StorageException(e); + } + TrustManager[] tms = new TrustManager[] { ourTm }; + sc.init(null, tms, new SecureRandom()); + SSLSocketFactory socketFactory = new SSLSocketFactory(sc); + Scheme sch = new Scheme("https", 443, socketFactory); + conManager.getSchemeRegistry().register(sch); + } catch ( GeneralSecurityException e) { + throw new StorageException(e); + } + } + + private void initAuthentication(DefaultHttpClient client) + throws MalformedURLException { + if (username != null && password != null) { + URL endpointURL = new URL(endpoint); + // TODO: Maybe also limit to realm like 'Thermostat Realm' or such? + AuthScope scope = new AuthScope(endpointURL.getHost(), + endpointURL.getPort()); + Credentials creds = new UsernamePasswordCredentials(username, + password); + client.getCredentialsProvider().setCredentials(scope, creds); + } + } + + private void ping() throws StorageException { + post(endpoint + "/ping", (HttpEntity) null).close(); + } + + private CloseableHttpEntity post(String url, List<NameValuePair> formparams) + throws StorageException { + try { + return postImpl(url, formparams); + } catch (IOException ex) { + throw new StorageException(ex); + } + } + + private CloseableHttpEntity postImpl(String url, + List<NameValuePair> formparams) throws IOException { + HttpEntity entity; + if (formparams != null) { + entity = new UrlEncodedFormEntity(formparams, "UTF-8"); + } else { + entity = null; + } + return postImpl(url, entity); + } + + private CloseableHttpEntity post(String url, HttpEntity entity) + throws StorageException { + try { + return postImpl(url, entity); + } catch (IOException ex) { + throw new StorageException(ex); + } + } + + private CloseableHttpEntity postImpl(String url, HttpEntity entity) + throws IOException { + HttpPost httpPost = new HttpPost(url); + if (entity != null) { + httpPost.setEntity(entity); + } + HttpResponse response = httpClient.execute(httpPost); + StatusLine status = response.getStatusLine(); + if (status.getStatusCode() != 200) { + throw new IOException("Server returned status: " + status); + } + + return new CloseableHttpEntity(response.getEntity()); + } + + private static InputStream getContent(HttpEntity entity) { + try { + return entity.getContent(); + } catch (IOException ex) { + throw new StorageException(ex); + } + } + + private static Reader getContentAsReader(HttpEntity entity) { + InputStream in = getContent(entity); + return new InputStreamReader(in); + } + + @Override + public void registerCategory(Category category) throws StorageException { + NameValuePair nameParam = new BasicNameValuePair("name", + category.getName()); + NameValuePair categoryParam = new BasicNameValuePair("category", + gson.toJson(category)); + List<NameValuePair> formparams = Arrays + .asList(nameParam, categoryParam); + try (CloseableHttpEntity entity = post(endpoint + "/register-category", + formparams)) { + Reader reader = getContentAsReader(entity); + Integer id = gson.fromJson(reader, Integer.class); + categoryIds.put(category, id); + } + } + + @Override + public Query createQuery() { + return new WebQuery(categoryIds); + } + + @Override + public Remove createRemove() { + return new WebRemove(categoryIds); + } + + @Override + public WebUpdate createUpdate() { + return new WebUpdate(categoryIds); + } + + @SuppressWarnings("unchecked") + @Override + public <T extends Pojo> Cursor<T> findAllPojos(Query query, + Class<T> resultClass) throws StorageException { + ((WebQuery) query).setResultClassName(resultClass.getName()); + NameValuePair queryParam = new BasicNameValuePair("query", + gson.toJson(query)); + List<NameValuePair> formparams = Arrays.asList(queryParam); + try (CloseableHttpEntity entity = post(endpoint + "/find-all", + formparams)) { + Reader reader = getContentAsReader(entity); + T[] result = (T[]) gson.fromJson(reader, + Array.newInstance(resultClass, 0).getClass()); + return new WebCursor<T>(result); + } + } + + @Override + public <T extends Pojo> T findPojo(Query query, Class<T> resultClass) + throws StorageException { + ((WebQuery) query).setResultClassName(resultClass.getName()); + NameValuePair queryParam = new BasicNameValuePair("query", + gson.toJson(query)); + List<NameValuePair> formparams = Arrays.asList(queryParam); + try (CloseableHttpEntity entity = post(endpoint + "/find-pojo", + formparams)) { + Reader reader = getContentAsReader(entity); + T result = gson.fromJson(reader, resultClass); + return result; + } + } + + @Override + public String getAgentId() { + return agentId.toString(); + } + + @Override + public Connection getConnection() { + return new WebConnection(); + } + + @Override + public long getCount(Category category) throws StorageException { + NameValuePair categoryParam = new BasicNameValuePair("category", + gson.toJson(categoryIds.get(category))); + List<NameValuePair> formparams = Arrays.asList(categoryParam); + try (CloseableHttpEntity entity = post(endpoint + "/get-count", + formparams)) { + Reader reader = getContentAsReader(entity); + long result = gson.fromJson(reader, Long.class); + return result; + } + } + + @Override + public InputStream loadFile(String name) throws StorageException { + NameValuePair fileParam = new BasicNameValuePair("file", name); + List<NameValuePair> formparams = Arrays.asList(fileParam); + CloseableHttpEntity entity = post(endpoint + "/load-file", formparams); + return new WebDataStream(entity); + } + + @Override + public void purge() throws StorageException { + post(endpoint + "/purge", (HttpEntity) null).close(); + } + + @Override + public void putPojo(Category category, boolean replace, AgentIdPojo pojo) + throws StorageException { + // TODO: This logic should probably be moved elsewhere. I.e. out of the + // Storage API. + if (pojo.getAgentId() == null) { + pojo.setAgentId(getAgentId()); + } + + int categoryId = categoryIds.get(category); + WebInsert insert = new WebInsert(categoryId, replace, pojo.getClass() + .getName()); + NameValuePair insertParam = new BasicNameValuePair("insert", + gson.toJson(insert)); + NameValuePair pojoParam = new BasicNameValuePair("pojo", + gson.toJson(pojo)); + List<NameValuePair> formparams = Arrays.asList(insertParam, pojoParam); + post(endpoint + "/put-pojo", formparams).close(); + } + + @Override + public void removePojo(Remove remove) throws StorageException { + NameValuePair removeParam = new BasicNameValuePair("remove", + gson.toJson(remove)); + List<NameValuePair> formparams = Arrays.asList(removeParam); + post(endpoint + "/remove-pojo", formparams).close(); + } + + @Override + public void saveFile(String name, InputStream in) throws StorageException { + InputStreamBody body = new InputStreamBody(in, name); + MultipartEntity mpEntity = new MultipartEntity(); + mpEntity.addPart("file", body); + post(endpoint + "/save-file", mpEntity).close(); + } + + @Override + public void setAgentId(UUID agentId) { + this.agentId = agentId; + } + + @Override + public void updatePojo(Update update) throws StorageException { + WebUpdate webUp = (WebUpdate) update; + List<WebUpdate.UpdateValue> updateValues = webUp.getUpdates(); + List<Object> values = new ArrayList<>(updateValues.size()); + for (WebUpdate.UpdateValue updateValue : updateValues) { + values.add(updateValue.getValue()); + } + + NameValuePair updateParam = new BasicNameValuePair("update", + gson.toJson(update)); + NameValuePair valuesParam = new BasicNameValuePair("values", + gson.toJson(values)); + List<NameValuePair> formparams = Arrays + .asList(updateParam, valuesParam); + post(endpoint + "/update-pojo", formparams).close(); + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public void setAuthConfig(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public AuthToken generateToken() throws StorageException { + byte[] token = new byte[256]; + random.nextBytes(token); + NameValuePair clientTokenParam = new BasicNameValuePair("client-token", Base64.encodeBase64String(token)); + List<NameValuePair> formparams = Arrays.asList(clientTokenParam); + try (CloseableHttpEntity entity = post(endpoint + "/generate-token", formparams)) { + byte[] authToken = EntityUtils.toByteArray(entity); + return new AuthToken(authToken, token); + } catch (IOException ex) { + throw new StorageException(ex); + } + } + + @Override + public boolean verifyToken(AuthToken authToken) { + byte[] clientToken = authToken.getClientToken(); + byte[] token = authToken.getToken(); + NameValuePair clientTokenParam = new BasicNameValuePair("client-token", Base64.encodeBase64String(clientToken)); + NameValuePair tokenParam = new BasicNameValuePair("token", Base64.encodeBase64String(token)); + List<NameValuePair> formparams = Arrays.asList(clientTokenParam, tokenParam); + HttpResponse response = null; + try { + HttpEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8"); + HttpPost httpPost = new HttpPost(endpoint + "/verify-token"); + httpPost.setEntity(entity); + response = httpClient.execute(httpPost); + StatusLine status = response.getStatusLine(); + return status.getStatusCode() == 200; + } catch (IOException ex) { + throw new StorageException(ex); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException ex) { + throw new StorageException(ex); + } + } + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorageProvider.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,34 @@ +package com.redhat.thermostat.web.client.internal; + +import com.redhat.thermostat.storage.config.AuthenticationConfiguration; +import com.redhat.thermostat.storage.config.StartupConfiguration; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.core.StorageProvider; + +public class WebStorageProvider implements StorageProvider { + + private StartupConfiguration config; + + @Override + public Storage createStorage() { + WebStorage storage = new WebStorage(config); + storage.setEndpoint(config.getDBConnectionString()); + if (config instanceof AuthenticationConfiguration) { + AuthenticationConfiguration authConf = (AuthenticationConfiguration) config; + storage.setAuthConfig(authConf.getUsername(), authConf.getPassword()); + } + return storage; + } + + @Override + public void setConfig(StartupConfiguration config) { + this.config = config; + } + + @Override + public boolean canHandleProtocol() { + // use http since this might be https at some point + return config.getDBConnectionString().startsWith("http"); + } + +}
--- a/web/client/src/test/java/com/redhat/thermostat/web/client/TestObj.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +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.web.client; - -import com.redhat.thermostat.storage.core.Entity; -import com.redhat.thermostat.storage.core.Persist; -import com.redhat.thermostat.storage.model.BasePojo; - -@Entity -public class TestObj extends BasePojo { - - - private String property1; - - @Persist - public void setProperty1(String property1) { - this.property1 = property1; - } - - @Persist - public String getProperty1() { - return property1; - } - - public boolean equals(Object o) { - if (! (o instanceof TestObj)) { - return false; - } - TestObj other = (TestObj) o; - return property1.equals(other.property1); - } -}
--- a/web/client/src/test/java/com/redhat/thermostat/web/client/WebStorageTest.java Mon Dec 10 14:30:36 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,543 +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.web.client; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.codec.binary.Base64; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; -import com.redhat.thermostat.storage.core.AuthToken; -import com.redhat.thermostat.storage.core.AuthToken; -import com.redhat.thermostat.storage.core.Categories; -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.Cursor; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.Query; -import com.redhat.thermostat.storage.core.Remove; -import com.redhat.thermostat.storage.core.Query.Criteria; -import com.redhat.thermostat.test.FreePortFinder; -import com.redhat.thermostat.test.FreePortFinder.TryPort; -import com.redhat.thermostat.web.common.Qualifier; -import com.redhat.thermostat.web.common.WebQuery; -import com.redhat.thermostat.web.common.WebInsert; -import com.redhat.thermostat.web.common.WebRemove; -import com.redhat.thermostat.web.common.WebUpdate; - -public class WebStorageTest { - - private Server server; - - private int port; - - private String requestBody; - - private String responseBody; - private Map<String,String> headers; - private String method; - private String requestURI; - private int responseStatus; - - private static Category category; - private static Key<String> key1; - private static Key<Integer> key2; - - private WebStorage storage; - - @BeforeClass - public static void setupCategory() { - key1 = new Key<>("property1", true); - key2 = new Key<>("property2", true); - category = new Category("test", key1); - } - - @AfterClass - public static void cleanupCategory() { - Categories.remove(category); - category = null; - key1 = null; - } - - @Before - public void setUp() throws Exception { - - port = FreePortFinder.findFreePort(new TryPort() { - @Override - public void tryPort(int port) throws Exception { - startServer(port); - } - }); - - storage = new WebStorage(); - storage.setEndpoint("http://localhost:" + port + "/"); - storage.setAgentId(new UUID(123, 456)); - headers = new HashMap<>(); - requestURI = null; - method = null; - responseStatus = HttpServletResponse.SC_OK; - registerCategory(); - } - - private void startServer(int port) throws Exception { - server = new Server(port); - server.setHandler(new AbstractHandler() { - - @Override - public void handle(String target, Request baseRequest, - HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - Enumeration<String> headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - headers.put(headerName, request.getHeader(headerName)); - } - - method = request.getMethod(); - requestURI = request.getRequestURI(); - - // Read request body. - StringBuilder body = new StringBuilder(); - Reader reader = request.getReader(); - while (true) { - int read = reader.read(); - if (read == -1) { - break; - } - body.append((char) read); - } - requestBody = body.toString(); - // Send response body. - response.setStatus(responseStatus); - if (responseBody != null) { - response.getWriter().write(responseBody); - } - baseRequest.setHandled(true); - } - }); - server.start(); - } - - @After - public void tearDown() throws Exception { - - headers = null; - requestURI = null; - method = null; - storage = null; - - server.stop(); - server.join(); - } - - private void registerCategory() { - - // Return 42 for categoryId. - Gson gson = new Gson(); - responseBody = gson.toJson(42); - - storage.registerCategory(category); - } - - @Test - public void testFindPojo() throws UnsupportedEncodingException, IOException { - - TestObj obj = new TestObj(); - obj.setProperty1("fluffor"); - Gson gson = new Gson(); - responseBody = gson.toJson(obj); - - Query query = storage.createQuery().from(category).where(key1, Criteria.EQUALS, "fluff"); - - TestObj result = storage.findPojo(query, TestObj.class); - StringReader reader = new StringReader(requestBody); - BufferedReader bufRead = new BufferedReader(reader); - String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); - String[] parts = line.split("="); - assertEquals("query", parts[0]); - WebQuery restQuery = gson.fromJson(parts[1], WebQuery.class); - - assertEquals(42, restQuery.getCategoryId()); - List<Qualifier<?>> qualifiers = restQuery.getQualifiers(); - assertEquals(1, qualifiers.size()); - Qualifier<?> qual = qualifiers.get(0); - assertEquals(new Key<String>("property1", true), qual.getKey()); - assertEquals(Criteria.EQUALS, qual.getCriteria()); - assertEquals("fluff", qual.getValue()); - - assertEquals("fluffor", result.getProperty1()); - } - - @Test - public void testFindAllPojos() throws UnsupportedEncodingException, IOException { - - TestObj obj1 = new TestObj(); - obj1.setProperty1("fluffor1"); - TestObj obj2 = new TestObj(); - obj2.setProperty1("fluffor2"); - Gson gson = new Gson(); - responseBody = gson.toJson(Arrays.asList(obj1, obj2)); - - Key<String> key1 = new Key<>("property1", true); - Query query = storage.createQuery().from(category).where(key1, Criteria.EQUALS, "fluff"); - - Cursor<TestObj> results = storage.findAllPojos(query, TestObj.class); - StringReader reader = new StringReader(requestBody); - BufferedReader bufRead = new BufferedReader(reader); - String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); - String[] parts = line.split("="); - assertEquals("query", parts[0]); - WebQuery restQuery = gson.fromJson(parts[1], WebQuery.class); - - assertEquals(42, restQuery.getCategoryId()); - List<Qualifier<?>> qualifiers = restQuery.getQualifiers(); - assertEquals(1, qualifiers.size()); - Qualifier<?> qual = qualifiers.get(0); - assertEquals(new Key<String>("property1", true), qual.getKey()); - assertEquals(Criteria.EQUALS, qual.getCriteria()); - assertEquals("fluff", qual.getValue()); - - assertTrue(results.hasNext()); - assertEquals("fluffor1", results.next().getProperty1()); - assertTrue(results.hasNext()); - assertEquals("fluffor2", results.next().getProperty1()); - assertFalse(results.hasNext()); - } - - @Test - public void testPut() throws IOException, JsonSyntaxException, ClassNotFoundException { - - TestObj obj = new TestObj(); - obj.setProperty1("fluff"); - - storage.putPojo(category, true, obj); - - Gson gson = new Gson(); - StringReader reader = new StringReader(requestBody); - BufferedReader bufRead = new BufferedReader(reader); - String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); - String [] params = line.split("&"); - assertEquals(2, params.length); - String[] parts = params[0].split("="); - assertEquals("insert", parts[0]); - WebInsert insert = gson.fromJson(parts[1], WebInsert.class); - assertEquals(42, insert.getCategoryId()); - assertEquals(true, insert.isReplace()); - assertEquals(TestObj.class.getName(), insert.getPojoClass()); - - parts = params[1].split("="); - assertEquals(2, parts.length); - assertEquals("pojo", parts[0]); - Object resultObj = gson.fromJson(parts[1], Class.forName(insert.getPojoClass())); - assertEquals(obj, resultObj); - } - - @Test - public void testCreateRemove() { - WebRemove remove = (WebRemove) storage.createRemove(); - assertNotNull(remove); - remove = remove.from(category); - assertEquals(42, remove.getCategoryId()); - assertNotNull(remove); - remove = remove.where(key1, "test"); - assertNotNull(remove); - List<Qualifier<?>> qualifiers = remove.getQualifiers(); - assertEquals(1, qualifiers.size()); - Qualifier<?> qualifier = qualifiers.get(0); - assertEquals(key1, qualifier.getKey()); - assertEquals(Criteria.EQUALS, qualifier.getCriteria()); - assertEquals("test", qualifier.getValue()); - } - - @Test - public void testRemovePojo() throws UnsupportedEncodingException, IOException { - Remove remove = storage.createRemove().from(category).where(key1, "test"); - storage.removePojo(remove); - - Gson gson = new Gson(); - StringReader reader = new StringReader(requestBody); - BufferedReader bufRead = new BufferedReader(reader); - String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); - String[] parts = line.split("="); - assertEquals("remove", parts[0]); - WebRemove actualRemove = gson.fromJson(parts[1], WebRemove.class); - - assertEquals(42, actualRemove.getCategoryId()); - List<Qualifier<?>> qualifiers = actualRemove.getQualifiers(); - assertEquals(1, qualifiers.size()); - Qualifier<?> qualifier = qualifiers.get(0); - assertEquals(key1, qualifier.getKey()); - assertEquals(Criteria.EQUALS, qualifier.getCriteria()); - assertEquals("test", qualifier.getValue()); - } - - @Test - public void testCreateUpdate() { - WebUpdate update = (WebUpdate) storage.createUpdate(); - assertNotNull(update); - update = update.from(category); - assertEquals(42, update.getCategoryId()); - assertNotNull(update); - update = update.where(key1, "test"); - assertNotNull(update); - List<Qualifier<?>> qualifiers = update.getQualifiers(); - assertEquals(1, qualifiers.size()); - Qualifier<?> qualifier = qualifiers.get(0); - assertEquals(key1, qualifier.getKey()); - assertEquals(Criteria.EQUALS, qualifier.getCriteria()); - assertEquals("test", qualifier.getValue()); - update = update.set(key1, "fluff"); - assertNotNull(update); - List<WebUpdate.UpdateValue> updates = update.getUpdates(); - assertEquals(1, updates.size()); - assertEquals("fluff", updates.get(0).getValue()); - assertEquals(key1, updates.get(0).getKey()); - assertEquals("java.lang.String", updates.get(0).getValueClass()); - } - - @Test - public void testUpdate() throws UnsupportedEncodingException, IOException, JsonSyntaxException, ClassNotFoundException { - - WebUpdate update = storage.createUpdate().from(category).where(key1, "test").set(key1, "fluff").set(key2, 42); - storage.updatePojo(update); - - Gson gson = new Gson(); - StringReader reader = new StringReader(requestBody); - BufferedReader bufRead = new BufferedReader(reader); - String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); - String [] params = line.split("&"); - assertEquals(2, params.length); - String[] parts = params[0].split("="); - assertEquals("update", parts[0]); - WebUpdate receivedUpdate = gson.fromJson(parts[1], WebUpdate.class); - assertEquals(42, receivedUpdate.getCategoryId()); - - List<WebUpdate.UpdateValue> updates = receivedUpdate.getUpdates(); - assertEquals(2, updates.size()); - - WebUpdate.UpdateValue update1 = updates.get(0); - assertEquals(key1, update1.getKey()); - assertEquals("java.lang.String", update1.getValueClass()); - assertNull(update1.getValue()); - - WebUpdate.UpdateValue update2 = updates.get(1); - assertEquals(key2, update2.getKey()); - assertEquals("java.lang.Integer", update2.getValueClass()); - assertNull(update2.getValue()); - - List<Qualifier<?>> qualifiers = receivedUpdate.getQualifiers(); - assertEquals(1, qualifiers.size()); - Qualifier<?> qualifier = qualifiers.get(0); - assertEquals(key1, qualifier.getKey()); - assertEquals(Criteria.EQUALS, qualifier.getCriteria()); - assertEquals("test", qualifier.getValue()); - - parts = params[1].split("="); - assertEquals(2, parts.length); - assertEquals("values", parts[0]); - JsonParser jsonParser = new JsonParser(); - JsonArray jsonArray = jsonParser.parse(parts[1]).getAsJsonArray(); - String value1 = gson.fromJson(jsonArray.get(0), String.class); - assertEquals("fluff", value1); - int value2 = gson.fromJson(jsonArray.get(1), Integer.class); - assertEquals(42, value2); - } - - @Test - public void testGetCount() throws UnsupportedEncodingException, IOException { - - Gson gson = new Gson(); - responseBody = gson.toJson(12345); - - long result = storage.getCount(category); - - StringReader reader = new StringReader(requestBody); - BufferedReader bufRead = new BufferedReader(reader); - String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); - String[] parts = line.split("="); - assertEquals("category", parts[0]); - assertEquals("42", parts[1]); - assertEquals(12345, result); - } - - @Test - public void testSaveFile() { - String data = "Hello World"; - ByteArrayInputStream in = new ByteArrayInputStream(data.getBytes()); - storage.saveFile("fluff", in); - assertEquals("chunked", headers.get("Transfer-Encoding")); - String contentType = headers.get("Content-Type"); - assertTrue(contentType.startsWith("multipart/form-data; boundary=")); - String boundary = contentType.split("boundary=")[1]; - String[] lines = requestBody.split("\n"); - assertEquals("--" + boundary, lines[0].trim()); - assertEquals("Content-Disposition: form-data; name=\"file\"; filename=\"fluff\"", lines[1].trim()); - assertEquals("Content-Type: application/octet-stream", lines[2].trim()); - assertEquals("Content-Transfer-Encoding: binary", lines[3].trim()); - assertEquals("", lines[4].trim()); - assertEquals("Hello World", lines[5].trim()); - assertEquals("--" + boundary + "--", lines[6].trim()); - - } - - @Test - public void testLoadFile() throws IOException { - responseBody = "Hello World"; - InputStream in = storage.loadFile("fluff"); - assertEquals("file=fluff", requestBody.trim()); - byte[] data = new byte[11]; - int totalRead = 0; - while (totalRead < 11) { - int read = in.read(data, totalRead, 11 - totalRead); - if (read < 0) { - fail(); - } - totalRead += read; - } - assertEquals("Hello World", new String(data)); - - } - - @Test - public void testPurge() { - storage.purge(); - assertEquals("POST", method); - assertTrue(requestURI.endsWith("/purge")); - } - - @Test - public void testGenerateToken() throws UnsupportedEncodingException { - responseBody = "flufftoken"; - - AuthToken authToken = storage.generateToken(); - - assertTrue(requestURI.endsWith("/generate-token")); - assertEquals("POST", method); - - String[] requestParts = requestBody.split("="); - assertEquals("client-token", requestParts[0]); - String clientTokenParam = URLDecoder.decode(requestParts[1], "UTF-8"); - byte[] clientToken = Base64.decodeBase64(clientTokenParam); - assertEquals(256, clientToken.length); - - assertTrue(authToken instanceof AuthToken); - AuthToken token = (AuthToken) authToken; - byte[] tokenBytes = token.getToken(); - assertEquals("flufftoken", new String(tokenBytes)); - - assertTrue(Arrays.equals(clientToken, token.getClientToken())); - - // Send another request and verify that we send a different client-token every time. - storage.generateToken(); - - requestParts = requestBody.split("="); - assertEquals("client-token", requestParts[0]); - clientTokenParam = URLDecoder.decode(requestParts[1], "UTF-8"); - byte[] clientToken2 = Base64.decodeBase64(clientTokenParam); - assertFalse(Arrays.equals(clientToken, clientToken2)); - - } - - @Test - public void testVerifyToken() throws UnsupportedEncodingException { - byte[] token = "stuff".getBytes(); - byte[] clientToken = "fluff".getBytes(); - AuthToken authToken = new AuthToken(token, clientToken); - - responseStatus = 200; - boolean ok = storage.verifyToken(authToken); - assertTrue(requestURI.endsWith("/verify-token")); - assertEquals("POST", method); - String[] requestParts = requestBody.split("&"); - assertEquals(2, requestParts.length); - String[] clientTokenParts = requestParts[0].split("="); - assertEquals(2, clientTokenParts.length); - assertEquals("client-token", clientTokenParts[0]); - String urlDecoded = URLDecoder.decode(clientTokenParts[1], "UTF-8"); - String base64decoded = new String(Base64.decodeBase64(urlDecoded)); - assertEquals("fluff", base64decoded); - String[] authTokenParts = requestParts[1].split("="); - assertEquals(2, authTokenParts.length); - assertEquals("token", authTokenParts[0]); - urlDecoded = URLDecoder.decode(authTokenParts[1], "UTF-8"); - base64decoded = new String(Base64.decodeBase64(urlDecoded)); - assertEquals("stuff", base64decoded); - assertTrue(ok); - - // Try another one in which verification fails. - responseStatus = 401; - ok = storage.verifyToken(authToken); - assertFalse(ok); - - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/test/java/com/redhat/thermostat/web/client/internal/CustomX509TrustManagerTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,140 @@ +/* + * 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.web.client.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +import java.io.File; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +import org.junit.Test; + +import com.redhat.thermostat.web.client.internal.CustomX509TrustManager; + +/** + * This trust manager test uses files in src/test/resources. Files are as + * follows: + * + * empty.keystore => emtpy file (not a real keystore file) + * + * ca.crt => a openssl generated X509 certificate (representing a custom CA) + * + * test_ca.keystore => a Java keystore file with ca.crt imported. Uses keystore + * password "testpassword". Used command sequence to generate this file: + * + * $ keytool -genkey -alias com.redhat -keyalg RSA -keystore test_ca.keystore -keysize 2048 + * $ keytool -import -trustcacerts -alias root -file ca.crt -keystore test_ca.keystore + * + * + */ +public class CustomX509TrustManagerTest { + + @Test + public void testEmptyDefaultOur() { + X509TrustManager tm = new CustomX509TrustManager( + (X509TrustManager) null, (X509TrustManager) null); + assertEquals(0, tm.getAcceptedIssuers().length); + try { + tm.checkClientTrusted(null, null); + } catch (Exception e) { + fail("Should not have thrown exception"); + } + try { + tm.checkServerTrusted(null, null); + fail("Expected exception since there aren't any trust managers available"); + } catch (CertificateException e) { + // pass + } + } + + @Test + public void testLoadEmptyTrustStoreForOur() { + File emptyKeyStore = new File(this.getClass() + .getResource("/empty.keystore").getFile()); + X509TrustManager tm = new CustomX509TrustManager(null, emptyKeyStore, + ""); + assertEquals(0, tm.getAcceptedIssuers().length); + try { + tm.checkClientTrusted(null, null); + } catch (Exception e) { + fail("Should not have thrown exception"); + } + try { + X509Certificate dummyCert = mock(X509Certificate.class); + tm.checkServerTrusted(new X509Certificate[] { dummyCert }, "RSA"); + fail("Expected exception since there aren't any trust managers available"); + } catch (CertificateException e) { + // pass + } + } + + @Test + public void testLoadEmptyTrustStoreForOurDefaultAsUsual() throws Exception { + File emptyKeyStore = new File(this.getClass() + .getResource("/empty.keystore").getFile()); + X509TrustManager tm = new CustomX509TrustManager(emptyKeyStore, ""); + // Default list should not be empty + assertTrue(tm.getAcceptedIssuers().length > 0); + try { + tm.checkClientTrusted(null, null); + } catch (Exception e) { + fail("Should not have thrown exception"); + } + } + + @Test + public void canGetCustomCaCertFromOurTrustManager() { + File ourKeyStore = new File(this.getClass() + .getResource("/test_ca.keystore").getFile()); + X509TrustManager tm = new CustomX509TrustManager((X509TrustManager)null, ourKeyStore, "testpassword"); + // keystore contains private key of itself + imported CA cert + assertEquals(2, tm.getAcceptedIssuers().length); + String issuerNameCustomCA = "1.2.840.113549.1.9.1=#16126a6572626f6161407265646861742e636f6d,CN=test.example.com,O=Red Hat Inc.,L=Saalfelden,ST=Salzburg,C=AT"; + String issuerNameKeystoreCA = "CN=Unknown,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown"; + assertEquals(issuerNameCustomCA, tm.getAcceptedIssuers()[0] + .getIssuerX500Principal().getName()); + assertEquals(issuerNameKeystoreCA, tm.getAcceptedIssuers()[1] + .getIssuerX500Principal().getName()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/test/java/com/redhat/thermostat/web/client/internal/SSLKeystoreConfigurationTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,67 @@ +/* + * 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.web.client.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Test; + +import com.redhat.thermostat.web.client.internal.SSLKeystoreConfiguration; + +public class SSLKeystoreConfigurationTest { + + @Test + public void canGetKeystoreFileFromProps() throws Exception { + File clientProps = new File(this.getClass().getResource("/client.properties").getFile()); + SSLKeystoreConfiguration.initClientProperties(clientProps); + String keystorePath = "/path/to/thermostat.keystore"; + String keystorePwd = "some password"; + assertEquals(keystorePath, SSLKeystoreConfiguration.getKeystoreFile().getAbsolutePath()); + assertEquals(keystorePwd, SSLKeystoreConfiguration.getKeyStorePassword()); + } + + @Test + public void notExistingPropertiesFileReturnsNull() throws Exception { + File clientProps = new File("i/am/not/there/file.txt"); + SSLKeystoreConfiguration.initClientProperties(clientProps); + assertTrue(SSLKeystoreConfiguration.getKeystoreFile() == null); + assertEquals("", SSLKeystoreConfiguration.getKeyStorePassword()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/test/java/com/redhat/thermostat/web/client/internal/TestObj.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,67 @@ +/* + * 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.web.client.internal; + +import com.redhat.thermostat.storage.core.Entity; +import com.redhat.thermostat.storage.core.Persist; +import com.redhat.thermostat.storage.model.BasePojo; + +@Entity +public class TestObj extends BasePojo { + + + private String property1; + + @Persist + public void setProperty1(String property1) { + this.property1 = property1; + } + + @Persist + public String getProperty1() { + return property1; + } + + public boolean equals(Object o) { + if (! (o instanceof TestObj)) { + return false; + } + TestObj other = (TestObj) o; + return property1.equals(other.property1); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,571 @@ +/* + * 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.web.client.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.codec.binary.Base64; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import com.redhat.thermostat.storage.core.AuthToken; +import com.redhat.thermostat.storage.core.AuthToken; +import com.redhat.thermostat.storage.config.StartupConfiguration; +import com.redhat.thermostat.storage.core.Categories; +import com.redhat.thermostat.storage.core.Category; +import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.Query; +import com.redhat.thermostat.storage.core.Query.Criteria; +import com.redhat.thermostat.storage.core.Remove; +import com.redhat.thermostat.test.FreePortFinder; +import com.redhat.thermostat.test.FreePortFinder.TryPort; +import com.redhat.thermostat.web.common.Qualifier; +import com.redhat.thermostat.web.common.WebInsert; +import com.redhat.thermostat.web.common.WebQuery; +import com.redhat.thermostat.web.common.WebRemove; +import com.redhat.thermostat.web.common.WebUpdate; + +public class WebStorageTest { + + private Server server; + + private int port; + + private String requestBody; + + private String responseBody; + private Map<String,String> headers; + private String method; + private String requestURI; + private int responseStatus; + + private static Category category; + private static Key<String> key1; + private static Key<Integer> key2; + + private WebStorage storage; + + @BeforeClass + public static void setupCategory() { + key1 = new Key<>("property1", true); + key2 = new Key<>("property2", true); + category = new Category("test", key1); + } + + @AfterClass + public static void cleanupCategory() { + Categories.remove(category); + category = null; + key1 = null; + } + + @Before + public void setUp() throws Exception { + + port = FreePortFinder.findFreePort(new TryPort() { + @Override + public void tryPort(int port) throws Exception { + startServer(port); + } + }); + + StartupConfiguration config = new StartupConfiguration() { + + @Override + public String getDBConnectionString() { + return "http://fluff.example.org"; + } + }; + storage = new WebStorage(config); + storage.setEndpoint("http://localhost:" + port + "/"); + storage.setAgentId(new UUID(123, 456)); + headers = new HashMap<>(); + requestURI = null; + method = null; + responseStatus = HttpServletResponse.SC_OK; + registerCategory(); + } + + private void startServer(int port) throws Exception { + server = new Server(port); + server.setHandler(new AbstractHandler() { + + @Override + public void handle(String target, Request baseRequest, + HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + Enumeration<String> headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + headers.put(headerName, request.getHeader(headerName)); + } + + method = request.getMethod(); + requestURI = request.getRequestURI(); + + // Read request body. + StringBuilder body = new StringBuilder(); + Reader reader = request.getReader(); + while (true) { + int read = reader.read(); + if (read == -1) { + break; + } + body.append((char) read); + } + requestBody = body.toString(); + // Send response body. + response.setStatus(responseStatus); + if (responseBody != null) { + response.getWriter().write(responseBody); + } + baseRequest.setHandled(true); + } + }); + server.start(); + } + + @After + public void tearDown() throws Exception { + + headers = null; + requestURI = null; + method = null; + storage = null; + + server.stop(); + server.join(); + } + + private void registerCategory() { + + // Return 42 for categoryId. + Gson gson = new Gson(); + responseBody = gson.toJson(42); + + storage.registerCategory(category); + } + + @Test + public void testFindPojo() throws UnsupportedEncodingException, IOException { + + TestObj obj = new TestObj(); + obj.setProperty1("fluffor"); + Gson gson = new Gson(); + responseBody = gson.toJson(obj); + + Query query = storage.createQuery().from(category).where(key1, Criteria.EQUALS, "fluff"); + + TestObj result = storage.findPojo(query, TestObj.class); + StringReader reader = new StringReader(requestBody); + BufferedReader bufRead = new BufferedReader(reader); + String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); + String[] parts = line.split("="); + assertEquals("query", parts[0]); + WebQuery restQuery = gson.fromJson(parts[1], WebQuery.class); + + assertEquals(42, restQuery.getCategoryId()); + List<Qualifier<?>> qualifiers = restQuery.getQualifiers(); + assertEquals(1, qualifiers.size()); + Qualifier<?> qual = qualifiers.get(0); + assertEquals(new Key<String>("property1", true), qual.getKey()); + assertEquals(Criteria.EQUALS, qual.getCriteria()); + assertEquals("fluff", qual.getValue()); + + assertEquals("fluffor", result.getProperty1()); + } + + @Test + public void testFindAllPojos() throws UnsupportedEncodingException, IOException { + + TestObj obj1 = new TestObj(); + obj1.setProperty1("fluffor1"); + TestObj obj2 = new TestObj(); + obj2.setProperty1("fluffor2"); + Gson gson = new Gson(); + responseBody = gson.toJson(Arrays.asList(obj1, obj2)); + + Key<String> key1 = new Key<>("property1", true); + Query query = storage.createQuery().from(category).where(key1, Criteria.EQUALS, "fluff"); + + Cursor<TestObj> results = storage.findAllPojos(query, TestObj.class); + StringReader reader = new StringReader(requestBody); + BufferedReader bufRead = new BufferedReader(reader); + String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); + String[] parts = line.split("="); + assertEquals("query", parts[0]); + WebQuery restQuery = gson.fromJson(parts[1], WebQuery.class); + + assertEquals(42, restQuery.getCategoryId()); + List<Qualifier<?>> qualifiers = restQuery.getQualifiers(); + assertEquals(1, qualifiers.size()); + Qualifier<?> qual = qualifiers.get(0); + assertEquals(new Key<String>("property1", true), qual.getKey()); + assertEquals(Criteria.EQUALS, qual.getCriteria()); + assertEquals("fluff", qual.getValue()); + + assertTrue(results.hasNext()); + assertEquals("fluffor1", results.next().getProperty1()); + assertTrue(results.hasNext()); + assertEquals("fluffor2", results.next().getProperty1()); + assertFalse(results.hasNext()); + } + + @Test + public void testPut() throws IOException, JsonSyntaxException, ClassNotFoundException { + + TestObj obj = new TestObj(); + obj.setProperty1("fluff"); + + storage.putPojo(category, true, obj); + + Gson gson = new Gson(); + StringReader reader = new StringReader(requestBody); + BufferedReader bufRead = new BufferedReader(reader); + String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); + String [] params = line.split("&"); + assertEquals(2, params.length); + String[] parts = params[0].split("="); + assertEquals("insert", parts[0]); + WebInsert insert = gson.fromJson(parts[1], WebInsert.class); + assertEquals(42, insert.getCategoryId()); + assertEquals(true, insert.isReplace()); + assertEquals(TestObj.class.getName(), insert.getPojoClass()); + + parts = params[1].split("="); + assertEquals(2, parts.length); + assertEquals("pojo", parts[0]); + Object resultObj = gson.fromJson(parts[1], Class.forName(insert.getPojoClass())); + assertEquals(obj, resultObj); + } + + @Test + public void testCreateRemove() { + WebRemove remove = (WebRemove) storage.createRemove(); + assertNotNull(remove); + remove = remove.from(category); + assertEquals(42, remove.getCategoryId()); + assertNotNull(remove); + remove = remove.where(key1, "test"); + assertNotNull(remove); + List<Qualifier<?>> qualifiers = remove.getQualifiers(); + assertEquals(1, qualifiers.size()); + Qualifier<?> qualifier = qualifiers.get(0); + assertEquals(key1, qualifier.getKey()); + assertEquals(Criteria.EQUALS, qualifier.getCriteria()); + assertEquals("test", qualifier.getValue()); + } + + @Test + public void testRemovePojo() throws UnsupportedEncodingException, IOException { + Remove remove = storage.createRemove().from(category).where(key1, "test"); + storage.removePojo(remove); + + Gson gson = new Gson(); + StringReader reader = new StringReader(requestBody); + BufferedReader bufRead = new BufferedReader(reader); + String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); + String[] parts = line.split("="); + assertEquals("remove", parts[0]); + WebRemove actualRemove = gson.fromJson(parts[1], WebRemove.class); + + assertEquals(42, actualRemove.getCategoryId()); + List<Qualifier<?>> qualifiers = actualRemove.getQualifiers(); + assertEquals(1, qualifiers.size()); + Qualifier<?> qualifier = qualifiers.get(0); + assertEquals(key1, qualifier.getKey()); + assertEquals(Criteria.EQUALS, qualifier.getCriteria()); + assertEquals("test", qualifier.getValue()); + } + + @Test + public void testCreateUpdate() { + WebUpdate update = (WebUpdate) storage.createUpdate(); + assertNotNull(update); + update = update.from(category); + assertEquals(42, update.getCategoryId()); + assertNotNull(update); + update = update.where(key1, "test"); + assertNotNull(update); + List<Qualifier<?>> qualifiers = update.getQualifiers(); + assertEquals(1, qualifiers.size()); + Qualifier<?> qualifier = qualifiers.get(0); + assertEquals(key1, qualifier.getKey()); + assertEquals(Criteria.EQUALS, qualifier.getCriteria()); + assertEquals("test", qualifier.getValue()); + update = update.set(key1, "fluff"); + assertNotNull(update); + List<WebUpdate.UpdateValue> updates = update.getUpdates(); + assertEquals(1, updates.size()); + assertEquals("fluff", updates.get(0).getValue()); + assertEquals(key1, updates.get(0).getKey()); + assertEquals("java.lang.String", updates.get(0).getValueClass()); + } + + @Test + public void testUpdate() throws UnsupportedEncodingException, IOException, JsonSyntaxException, ClassNotFoundException { + + WebUpdate update = storage.createUpdate().from(category).where(key1, "test").set(key1, "fluff").set(key2, 42); + storage.updatePojo(update); + + Gson gson = new Gson(); + StringReader reader = new StringReader(requestBody); + BufferedReader bufRead = new BufferedReader(reader); + String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); + String [] params = line.split("&"); + assertEquals(2, params.length); + String[] parts = params[0].split("="); + assertEquals("update", parts[0]); + WebUpdate receivedUpdate = gson.fromJson(parts[1], WebUpdate.class); + assertEquals(42, receivedUpdate.getCategoryId()); + + List<WebUpdate.UpdateValue> updates = receivedUpdate.getUpdates(); + assertEquals(2, updates.size()); + + WebUpdate.UpdateValue update1 = updates.get(0); + assertEquals(key1, update1.getKey()); + assertEquals("java.lang.String", update1.getValueClass()); + assertNull(update1.getValue()); + + WebUpdate.UpdateValue update2 = updates.get(1); + assertEquals(key2, update2.getKey()); + assertEquals("java.lang.Integer", update2.getValueClass()); + assertNull(update2.getValue()); + + List<Qualifier<?>> qualifiers = receivedUpdate.getQualifiers(); + assertEquals(1, qualifiers.size()); + Qualifier<?> qualifier = qualifiers.get(0); + assertEquals(key1, qualifier.getKey()); + assertEquals(Criteria.EQUALS, qualifier.getCriteria()); + assertEquals("test", qualifier.getValue()); + + parts = params[1].split("="); + assertEquals(2, parts.length); + assertEquals("values", parts[0]); + JsonParser jsonParser = new JsonParser(); + JsonArray jsonArray = jsonParser.parse(parts[1]).getAsJsonArray(); + String value1 = gson.fromJson(jsonArray.get(0), String.class); + assertEquals("fluff", value1); + int value2 = gson.fromJson(jsonArray.get(1), Integer.class); + assertEquals(42, value2); + } + + @Test + public void testGetCount() throws UnsupportedEncodingException, IOException { + + Gson gson = new Gson(); + responseBody = gson.toJson(12345); + + long result = storage.getCount(category); + + StringReader reader = new StringReader(requestBody); + BufferedReader bufRead = new BufferedReader(reader); + String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); + String[] parts = line.split("="); + assertEquals("category", parts[0]); + assertEquals("42", parts[1]); + assertEquals(12345, result); + } + + @Test + public void testSaveFile() { + String data = "Hello World"; + ByteArrayInputStream in = new ByteArrayInputStream(data.getBytes()); + storage.saveFile("fluff", in); + assertEquals("chunked", headers.get("Transfer-Encoding")); + String contentType = headers.get("Content-Type"); + assertTrue(contentType.startsWith("multipart/form-data; boundary=")); + String boundary = contentType.split("boundary=")[1]; + String[] lines = requestBody.split("\n"); + assertEquals("--" + boundary, lines[0].trim()); + assertEquals("Content-Disposition: form-data; name=\"file\"; filename=\"fluff\"", lines[1].trim()); + assertEquals("Content-Type: application/octet-stream", lines[2].trim()); + assertEquals("Content-Transfer-Encoding: binary", lines[3].trim()); + assertEquals("", lines[4].trim()); + assertEquals("Hello World", lines[5].trim()); + assertEquals("--" + boundary + "--", lines[6].trim()); + + } + + @Test + public void testLoadFile() throws IOException { + responseBody = "Hello World"; + InputStream in = storage.loadFile("fluff"); + assertEquals("file=fluff", requestBody.trim()); + byte[] data = new byte[11]; + int totalRead = 0; + while (totalRead < 11) { + int read = in.read(data, totalRead, 11 - totalRead); + if (read < 0) { + fail(); + } + totalRead += read; + } + assertEquals("Hello World", new String(data)); + + } + + @Test + public void testPurge() { + storage.purge(); + assertEquals("POST", method); + assertTrue(requestURI.endsWith("/purge")); + } + + @Test + public void testGenerateToken() throws UnsupportedEncodingException { + responseBody = "flufftoken"; + + AuthToken authToken = storage.generateToken(); + + assertTrue(requestURI.endsWith("/generate-token")); + assertEquals("POST", method); + + String[] requestParts = requestBody.split("="); + assertEquals("client-token", requestParts[0]); + String clientTokenParam = URLDecoder.decode(requestParts[1], "UTF-8"); + byte[] clientToken = Base64.decodeBase64(clientTokenParam); + assertEquals(256, clientToken.length); + + assertTrue(authToken instanceof AuthToken); + AuthToken token = (AuthToken) authToken; + byte[] tokenBytes = token.getToken(); + assertEquals("flufftoken", new String(tokenBytes)); + + assertTrue(Arrays.equals(clientToken, token.getClientToken())); + + // Send another request and verify that we send a different client-token every time. + storage.generateToken(); + + requestParts = requestBody.split("="); + assertEquals("client-token", requestParts[0]); + clientTokenParam = URLDecoder.decode(requestParts[1], "UTF-8"); + byte[] clientToken2 = Base64.decodeBase64(clientTokenParam); + assertFalse(Arrays.equals(clientToken, clientToken2)); + + } + + @Test + public void canSSLEnableClient() { + StartupConfiguration config = new StartupConfiguration() { + + @Override + public String getDBConnectionString() { + return "https://onlyHttpsPrefixUsed.example.com"; + } + }; + storage = new WebStorage(config); + HttpClient client = storage.httpClient; + SchemeRegistry schemeReg = client.getConnectionManager().getSchemeRegistry(); + Scheme scheme = schemeReg.getScheme("https"); + assertNotNull(scheme); + assertEquals(443, scheme.getDefaultPort()); + } + + @Test + public void testVerifyToken() throws UnsupportedEncodingException { + byte[] token = "stuff".getBytes(); + byte[] clientToken = "fluff".getBytes(); + AuthToken authToken = new AuthToken(token, clientToken); + + responseStatus = 200; + boolean ok = storage.verifyToken(authToken); + assertTrue(requestURI.endsWith("/verify-token")); + assertEquals("POST", method); + String[] requestParts = requestBody.split("&"); + assertEquals(2, requestParts.length); + String[] clientTokenParts = requestParts[0].split("="); + assertEquals(2, clientTokenParts.length); + assertEquals("client-token", clientTokenParts[0]); + String urlDecoded = URLDecoder.decode(clientTokenParts[1], "UTF-8"); + String base64decoded = new String(Base64.decodeBase64(urlDecoded)); + assertEquals("fluff", base64decoded); + String[] authTokenParts = requestParts[1].split("="); + assertEquals(2, authTokenParts.length); + assertEquals("token", authTokenParts[0]); + urlDecoded = URLDecoder.decode(authTokenParts[1], "UTF-8"); + base64decoded = new String(Base64.decodeBase64(urlDecoded)); + assertEquals("stuff", base64decoded); + assertTrue(ok); + + // Try another one in which verification fails. + responseStatus = 401; + ok = storage.verifyToken(authToken); + assertFalse(ok); + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/client/src/test/resources/ca.crt Mon Dec 10 15:05:37 2012 +0100 @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF6TCCA9GgAwIBAgIJAMqGn7zFkpycMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD +VQQGEwJBVDERMA8GA1UECAwIU2FsemJ1cmcxEzARBgNVBAcMClNhYWxmZWxkZW4x +FTATBgNVBAoMDFJlZCBIYXQgSW5jLjEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLmNv +bTEhMB8GCSqGSIb3DQEJARYSamVyYm9hYUByZWRoYXQuY29tMB4XDTEyMTIwMzEz +MTc1M1oXDTE3MTIwMzEzMTc1M1owgYoxCzAJBgNVBAYTAkFUMREwDwYDVQQIDAhT +YWx6YnVyZzETMBEGA1UEBwwKU2FhbGZlbGRlbjEVMBMGA1UECgwMUmVkIEhhdCBJ +bmMuMRkwFwYDVQQDDBB0ZXN0LmV4YW1wbGUuY29tMSEwHwYJKoZIhvcNAQkBFhJq +ZXJib2FhQHJlZGhhdC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDKdOtT5HmVdFWj05aTqWQPX9L2NA44/69hwsrIwQFHi09DD+BwobaTmRpWg3Tg +6qr+128AFlj2N8gYHyKELSxt1rsBV1yleTcpywfsmP7XkcZfu9ZKgmB4xyXuK4IQ +EYxGqeWIdGYFNncjFm7Na3i9aTJZqtGxGf+kkQX2OdzqMbiXg17AYxH21kqMbNPk +R2mhWUE4sT9Nfj+qO+iUqHCUMPusJY4xi/tGjLA9phih85ShYyqRBXXHiplzDCm+ +0W/puPodPVDo1FdfAwXExYaj8QoKmqCnGT9U++gMQ0b1xkRyuyImdqF9o63HoCMm +8/R7ye0QTU8yUXwRoujvowSiip6/FAfADe7QyhHmScP9uS2/tNR0daCKUBnkWnJf ++PvbzNCT3V4MTwvg3LvphhU1JKXLSdQiZkSsLLKjnfUFE6BuT2RNFlWsbnuF2NcK +H8ZDySDFL2LSeROPmi1lV2k+MScG2XOwKQXVQiU5GPgqNhhYvSClIk/kOagBoI74 +Ixq9hDW1viOHwKHZ315M3rNTg3Y4dq4eohUPP9iLmCFEBOW14MGDOMRKhzdFSUnG +UBVdUiesqi3zwOSZ0EeqU2SgvmQKwvN1imWqsZlvkKV8p9mnFNmF/idbbkq2EWN0 +q/Hg7ekq/6mdN2f3XTGf0F85pTUtWWKpoyUM2qmZ72H+hQIDAQABo1AwTjAdBgNV +HQ4EFgQUpknaZuFsL+AYna6uu5gSlKVUdbkwHwYDVR0jBBgwFoAUpknaZuFsL+AY +na6uu5gSlKVUdbkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAgEAyAlS +DJ+h4SO0mfZLU88JuRF8LoADD2u2eO890aIC1PtxIm9Osrfrc104LukCGpeLJvSZ +hee/yZklen6K7lEURCgYmHreDtWx6zkf+qGdhqq0+97cfzvnSH8pDwVJxzY8dr+Q +K2XSFOszkmVVNOnvu/GgHjcpOvXBNItnalrAqGgFi4cOGRzFl9x4iWUEMiUWERnP +ntsisNsG4yY8D50Vl/k9qrU02IAsNQ+pnjDI+8bPni4aDE6nGH1+aNmS0J1YBM0s +pPpK/W9GIxltARUfHI/mnvRNMCPeaG7ohJKBaIJkvibTa5Ux6zx1lNhJmDTHH4t+ +jMdoF5+90/AxYTnOQFb9oTPTX4+HgOhib9YtaxA7mXH5lVGFB19UrT7eFFNZeUFu +Du+HOCPSrtTgLUdrkix+znl7ai4SORYFGL/v7QDw9U3FCGgRWuKzyQasvm3xELea +3GXOuttDMWyfsAAsBWGpcJVN+YN23B75j1xN2b8JMvNNQChifWiFcztp7DsA1qxo +M4g88l6WlRFm7OKqi9ZMbPH9/LlmvAhak5fS4l6UI08v/bX9qUEWNo1RzBZlHA4U +3Xs9P3VF7h2pp0O3ucWm6nWAcp0CfvmaTnjof+cW8LDRHqEK0tWLEdBLg1TnUnnk +EUsxCO6b/aOvribtT0aDLRB6sF8MTAtOBkczmp4= +-----END CERTIFICATE-----