changeset 172:c69bd1a12299

Merge
author Roman Kennke <rkennke@redhat.com>
date Thu, 29 Mar 2012 23:54:17 +0200
parents ca53979873fb (current diff) e230498139ed (diff)
children cbe6f5b7e43c
files agent/src/main/java/com/redhat/thermostat/agent/Main.java
diffstat 55 files changed, 3395 insertions(+), 567 deletions(-) [+]
line wrap: on
line diff
--- a/agent/src/main/java/com/redhat/thermostat/agent/Agent.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/agent/Agent.java	Thu Mar 29 23:54:17 2012 +0200
@@ -39,11 +39,11 @@
 import java.util.UUID;
 import java.util.logging.Logger;
 
+import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
 import com.redhat.thermostat.agent.config.ConfigurationWatcher;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
 import com.redhat.thermostat.common.LaunchException;
-import com.redhat.thermostat.common.config.StartupConfiguration;
 import com.redhat.thermostat.common.storage.AgentInformation;
 import com.redhat.thermostat.common.storage.BackendInformation;
 import com.redhat.thermostat.common.storage.Storage;
@@ -58,16 +58,16 @@
 
     private final UUID id;
     private final BackendRegistry backendRegistry;
-    private final StartupConfiguration config;
+    private final AgentStartupConfiguration config;
 
     private Storage storage;
     private Thread configWatcherThread = null;
 
-    public Agent(BackendRegistry backendRegistry, StartupConfiguration config, Storage storage) {
+    public Agent(BackendRegistry backendRegistry, AgentStartupConfiguration config, Storage storage) {
         this(backendRegistry, UUID.randomUUID(), config, storage);
     }
 
-    public Agent(BackendRegistry registry, UUID agentId, StartupConfiguration config, Storage storage) {
+    public Agent(BackendRegistry registry, UUID agentId, AgentStartupConfiguration config, Storage storage) {
         this.id = agentId;
         this.backendRegistry = registry;
         this.config = config;
@@ -133,9 +133,10 @@
             configWatcherThread = null;
             storage.removeAgentInformation();
             stopBackends();
-            if (config.getLocalMode()) {
-                storage.purge();
-            }
+            // TODO
+//            if (config.getLocalMode()) {
+//                storage.purge();
+//            }
         } else {
             logger.warning("Attempt to stop agent which is not active");
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/agent/AgentApplication.java	Thu Mar 29 23:54:17 2012 +0200
@@ -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.agent;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+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.BackendLoadException;
+import com.redhat.thermostat.backend.BackendRegistry;
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.dao.Connection;
+import com.redhat.thermostat.common.dao.ConnectionProvider;
+import com.redhat.thermostat.common.dao.MongoConnectionProvider;
+import com.redhat.thermostat.common.storage.ConnectionFailedException;
+import com.redhat.thermostat.common.storage.MongoStorage;
+import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.tools.BasicApplication;
+
+public final class AgentApplication extends BasicApplication {
+
+    private AgentStartupConfiguration configuration;
+    private AgentOptionParser parser;
+    
+    @Override
+    public void parseArguments(List<String> args) throws InvalidConfigurationException {
+        configuration = AgentConfigsUtils.createAgentConfigs();
+        parser = new AgentOptionParser(configuration, args);
+        parser.parse();
+    }
+
+    @Override
+    public AgentStartupConfiguration getConfiguration() {
+        return configuration;
+    }
+    
+    private void runAgent() {
+        long startTime = System.currentTimeMillis();
+        configuration.setStartTime(startTime);
+        
+        if (configuration.isDebugConsole()) {
+            LoggingUtils.useDevelConsole();
+        }
+        
+        LoggingUtils.setGlobalLogLevel(configuration.getLogLevel());
+        Logger logger = LoggingUtils.getLogger(AgentApplication.class);
+
+        ConnectionProvider connProv = new MongoConnectionProvider(configuration);
+        Connection connection = connProv.createConnection();
+
+        Storage storage = new MongoStorage(connection);
+        try {
+            storage.connect();
+            logger.fine("Storage configured with database URI.");
+        } catch (ConnectionFailedException ex) {
+            logger.log(Level.SEVERE, "Could not initialize storage layer.", ex);
+            System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
+        }
+
+        BackendRegistry backendRegistry = null;
+        try {
+            backendRegistry = new BackendRegistry(configuration, storage);
+        } catch (BackendLoadException ble) {
+            logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", ble);
+            System.exit(Constants.EXIT_BACKEND_LOAD_ERROR);
+        }
+
+        Agent agent = new Agent(backendRegistry, configuration, storage);
+        storage.setAgentId(agent.getId());
+        try {
+            logger.fine("Starting agent.");
+            agent.start();
+        } 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.");
+
+        try {
+            System.in.read();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        agent.stop();
+        logger.fine("Agent stopped.");       
+    }
+    
+    @Override
+    public void run() {
+         if (!parser.isHelp()) {
+             runAgent();
+         }
+    }
+
+    public static void main(String[] args) throws InvalidConfigurationException {        
+        AgentApplication service = new AgentApplication();
+        service.parseArguments(Arrays.asList(args));
+        service.run();
+    }
+}
--- a/agent/src/main/java/com/redhat/thermostat/agent/Main.java	Thu Mar 29 23:52:18 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +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;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.backend.BackendLoadException;
-import com.redhat.thermostat.backend.BackendRegistry;
-import com.redhat.thermostat.common.Constants;
-import com.redhat.thermostat.common.LaunchException;
-import com.redhat.thermostat.common.config.StartupConfiguration;
-import com.redhat.thermostat.common.dao.Connection;
-import com.redhat.thermostat.common.dao.ConnectionProvider;
-import com.redhat.thermostat.common.dao.MongoConnectionProvider;
-import com.redhat.thermostat.common.dao.Connection.ConnectionType;
-import com.redhat.thermostat.common.storage.ConnectionFailedException;
-import com.redhat.thermostat.common.storage.MongoStorage;
-import com.redhat.thermostat.common.storage.Storage;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public final class Main {
-
-    private Main() {
-        throw new IllegalStateException("Should not be instantiated");
-    }
-
-    public static void main(String[] args) {
-        long startTimestamp = System.currentTimeMillis();
-
-        List<String> argsAsList = new ArrayList<String>(Arrays.asList(args));
-        while (argsAsList.contains(Constants.AGENT_ARGUMENT_DEVEL)) {
-            argsAsList.remove(Constants.AGENT_ARGUMENT_DEVEL);
-            LoggingUtils.useDevelConsole();
-        }
-
-        LoggingUtils.setGlobalLogLevel(Level.ALL);
-        Logger logger = LoggingUtils.getLogger(Main.class);
-
-        StartupConfiguration config = null;
-        try {
-            config = new StartupConfiguration(startTimestamp, argsAsList.toArray(new String[0]));
-        } catch (LaunchException le) {
-            logger.log(Level.SEVERE,
-                    "Unable to instantiate startup configuration.",
-                    le);
-            System.exit(Constants.EXIT_CONFIGURATION_ERROR);
-        }
-
-        LoggingUtils.setGlobalLogLevel(config.getLogLevel());
-
-        ConnectionProvider connProv = new MongoConnectionProvider(config);
-        Connection connection = connProv.createConnection();
-        if (config.getLocalMode()) {
-            connection.setType(ConnectionType.LOCAL);
-        } else {
-            connection.setType(ConnectionType.REMOTE);
-        }
-        
-        Storage storage = new MongoStorage(connection);
-        try {
-            storage.connect();
-            logger.fine("Storage configured with database URI.");
-        } catch (ConnectionFailedException ex) {
-            logger.log(Level.SEVERE, "Could not initialize storage layer.", ex);
-            System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
-        }
-
-        BackendRegistry backendRegistry = null;
-        try {
-            backendRegistry = new BackendRegistry(config, storage);
-        } catch (BackendLoadException ble) {
-            logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", ble);
-            System.exit(Constants.EXIT_BACKEND_LOAD_ERROR);
-        }
-
-        Agent agent = new Agent(backendRegistry, config, storage);
-        storage.setAgentId(agent.getId());
-        try {
-            logger.fine("Starting agent.");
-            agent.start();
-        } 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.");
-
-        try {
-            System.in.read();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-
-        agent.stop();
-        logger.fine("Agent stopped.");
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,136 @@
+/*
+ * 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.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.logging.Level;
+
+import com.redhat.thermostat.common.config.ConfigUtils;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+
+public class AgentConfigsUtils {
+
+    public static AgentStartupConfiguration createAgentConfigs() throws InvalidConfigurationException {
+        
+        AgentStartupConfiguration config = new AgentStartupConfiguration();
+        
+        File propertyFile = ConfigUtils.getAgentConfigurationFile();
+        
+        config.setLogLevel(Level.FINE);
+        readAndSetProperties(propertyFile, config);
+        
+        return config;
+    }
+    
+    private static void readAndSetProperties(File propertyFile, AgentStartupConfiguration configuration)
+            throws InvalidConfigurationException
+    {
+        Properties properties = new Properties();
+        try {
+            properties.load(new FileInputStream(propertyFile));
+            
+        } catch (IOException e) {
+            throw new InvalidConfigurationException(e);
+        }
+        
+        if (properties.containsKey(AgentProperties.BACKENDS.name())) {
+            // this is a command separated list of backends
+            String backends = properties.getProperty(AgentProperties.BACKENDS.name());
+            configuration.parseBackends(backends.split(","));
+            
+        } else {
+            throw new InvalidConfigurationException(AgentProperties.BACKENDS + " property missing");
+        }
+        
+        if (properties.containsKey(AgentProperties.LOG_LEVEL.name())) {
+            String logLevel = properties.getProperty(AgentProperties.LOG_LEVEL.name());
+            Level level = getLogLevel(logLevel);
+            configuration.setLogLevel(level);
+        }
+        
+        if (properties.containsKey(AgentProperties.DB_URL.name())) {
+            String db = properties.getProperty(AgentProperties.DB_URL.name());
+            configuration.setDatabaseURL(db);
+        }
+    }
+    
+    public static Level getLogLevel(String logLevel) {
+        
+        Level level = Level.FINE;
+        switch (logLevel.toUpperCase()) {
+        case "SEVERE":
+            level = Level.SEVERE;
+            break;
+            
+        case "INFO":
+            level = Level.INFO;
+            break;
+            
+        case "CONFIG":
+            level = Level.CONFIG;
+            break;
+            
+        case "FINE":
+            level = Level.FINE;
+            break;
+        
+        case "FINER":
+            level = Level.FINER;
+            break;
+        
+        case "FINEST":
+            level = Level.FINEST;
+            break;
+        
+        case "WARNING":
+            level = Level.WARNING;
+            break;
+        
+        case "ALL":
+            level = Level.ALL;
+            break;
+        
+        default:
+            break;
+        }
+        
+        return level;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/agent/config/AgentOptionParser.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,134 @@
+/*
+ * 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.config;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.logging.Level;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.config.ThermostatOptionParser;
+
+public class AgentOptionParser implements ThermostatOptionParser {
+
+    private AgentStartupConfiguration configuration;
+    private OptionParser parser;
+    private List<String> args;
+    
+    private boolean isHelp;
+    
+    public AgentOptionParser(AgentStartupConfiguration configuration, List<String> args) {
+        this.configuration = configuration;
+        this.args = args;
+        parser = new OptionParser();
+        isHelp = false;
+    }
+    
+    @Override
+    public void parse() throws InvalidConfigurationException {
+
+        parser.accepts(Args.DEBUG.option, Args.DEBUG.description);
+        parser.accepts(Args.HELP.option, Args.HELP.description);
+        
+        OptionSpec<String> logLevel =
+                parser.accepts(Args.LEVEL.option, Args.LEVEL.description).
+                      withRequiredArg();
+        OptionSpec<String> dbUrl =
+                parser.accepts(Args.DB.option, Args.DB.description).
+                      withRequiredArg();
+        
+        OptionSet options = parser.parse(args.toArray(new String[0]));
+        if (options.has(Args.HELP.option)) {
+            displayHelp();
+            isHelp = true;
+            return;
+        }
+        
+        if (options.has(Args.LEVEL.option)) {
+            String levelString = logLevel.value(options);
+            Level level = AgentConfigsUtils.getLogLevel(levelString);
+            configuration.setLogLevel(level);
+        }
+
+        configuration.setDebugConsole(options.has(Args.DEBUG.option));
+        
+        if (options.has(Args.DB.option)) {
+            String url = dbUrl.value(options);
+            configuration.setDatabaseURL(url);
+        } else {
+            if (configuration.getDBConnectionString() == null) {
+                System.err.println("database url not specified... must be " +
+                                   "either set in config or passed on " +
+                                   "the command line");
+                displayHelp();
+                isHelp = true;
+            }
+        }
+    }
+    
+    public boolean isHelp() {
+        return isHelp;
+    }
+    
+    @Override
+    public void displayHelp() {
+        try {
+            parser.printHelpOn(System.out);
+        } catch (IOException ignore) {}
+    }
+    
+    private static enum Args {
+        
+        // TODO: localize
+        LEVEL("logLevel", "log level"),
+        DB("dbUrl", "connect to the given url"),
+        DEBUG("debug", "launch with debug console enabled"),
+        HELP("help", "print this help and exit");
+        
+        private String option;
+        private String description;
+        
+        Args(String option, String description) {
+            this.option = option;
+            this.description = description;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Thu Mar 29 23:54:17 2012 +0200
@@ -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.agent.config;
+
+public enum AgentProperties {
+
+    // backend list, comma separated
+    BACKENDS,
+    LOG_LEVEL,
+    DEBUG_CONSOLE,
+    DB_URL
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,134 @@
+/*
+ * 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.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.logging.Level;
+
+import com.redhat.thermostat.backend.BackendID;
+import com.redhat.thermostat.backend.BackendsProperties;
+import com.redhat.thermostat.common.config.ConfigUtils;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+
+public class AgentStartupConfiguration implements StartupConfiguration {
+
+    private List<BackendID> backends;
+    
+    private Level logLevel;
+    private boolean debugConsole;
+    
+    private String url;
+    
+    private long startTime;
+    
+    AgentStartupConfiguration() {
+        this.backends = new ArrayList<>();
+    }
+    
+    @Override
+    public String getDBConnectionString() {
+        return url;
+    }
+
+    public Level getLogLevel() {
+        return this.logLevel;
+    }
+
+    void setLogLevel(Level level) {
+        this.logLevel = level;
+    }
+    
+    void parseBackends(String[] backendsList) throws InvalidConfigurationException {
+        backends.clear();
+        
+        for (String simpleName : backendsList) {
+            String backendName = simpleName.trim();
+            
+            // a file must exist, at least with the class name
+            File backendSettings = ConfigUtils.getBackendPropertyFile(backendName);
+            Properties backendProps = new Properties();
+            try {
+                backendProps.load(new FileInputStream(backendSettings));
+                
+            } catch (IOException e) {
+                throw new InvalidConfigurationException(e);
+            }
+            
+            String backendClass = backendProps.getProperty(BackendsProperties.BACKEND_CLASS.name());
+            if (backendClass == null) {
+                throw new InvalidConfigurationException("Class name not found for backend: " +
+                                                        backendName);
+            }
+            
+            BackendID backend = new BackendID(backendName, backendClass);
+            backends.add(backend);
+        }
+    }
+    
+    public List<BackendID> getBackends() {
+        return backends;
+    }
+
+    void setDebugConsole(boolean debugConsole) {
+        this.debugConsole = debugConsole;
+    }
+    
+    public boolean isDebugConsole() {
+        return debugConsole;
+    }
+
+    public void setDatabaseURL(String url) {
+        this.url = url;
+    }
+    
+    // TODO: that should be a friend, we only want the Service to set this value
+    public void setStartTime(long startTime) {
+        this.startTime = startTime;
+    }
+    
+    public long getStartTime() {
+        return startTime;
+    }
+}
--- a/agent/src/main/java/com/redhat/thermostat/backend/Backend.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/Backend.java	Thu Mar 29 23:54:17 2012 +0200
@@ -56,6 +56,12 @@
     private Storage storage = null;
     private boolean observeNewJvm = attachToNewProcessByDefault();
 
+    private String version;
+    private String vendor;
+    private String description;
+    
+    private BackendID id;
+    
     /**
      * 
      * @param configMap a map containing the settings that this backend has been configured with.
@@ -89,34 +95,67 @@
 
     /**
      * Set the named configuration to the given value.
+     * The basic special properties {@code name}, {@code version} and
+     * {@code description} are parsed here.
+     * 
+     * <br /><br />
+     * 
+     * Subclasses can just override the
+     * {@link #setConfigurationValueImpl(String, String)}
+     * method if they are not interested in parsing and setting those
+     * properties directly.
+     * 
      * @param name
      * @param value
      * @throws IllegalArgumentException if either the key does not refer to a valid configuration option
      *                                  for this backend or the value is not valid for the key
      */
     protected void setConfigurationValue(String name, String value) {
-        throw new IllegalArgumentException("Backend " + getName() + " does not support any specific configuration values.");
+        
+        if (name.equals(BackendsProperties.DESCRIPTION.name())) {
+            this.description = value;
+        } else if (name.equals(BackendsProperties.VERSION.name())) {
+            this.version = value;
+        } else if (name.equals(BackendsProperties.VENDOR.name())) {
+            this.vendor = value;
+        } else {
+            setConfigurationValueImpl(name, value);
+        }
     }
-
+    
+    /**
+     * Set the named configuration to the given value.
+     * By default, does nothing.
+     */
+    protected void setConfigurationValueImpl(String name, String value) {}
+    
     /**
      * @return the name of the {@link Backend}
      */
-    public abstract String getName();
+    public String getName() {
+        return id.getSimpleName();
+    }
 
     /**
      * @returns the description of the {@link Backend}
      */
-    public abstract String getDescription();
+    public String getDescription() {
+        return description;
+    }
 
     /**
      * @return the vendor of the {@link Backend}
      */
-    public abstract String getVendor();
+    public String getVendor() {
+        return vendor;
+    }
 
     /** 
      * @return the version of the {@link Backend}
      */
-    public abstract String getVersion();
+    public String getVersion() {
+        return version;
+    }
 
     /** Get a map containing the current settings of this backend.
      * Implementors of this abstract class which have some settings that
@@ -196,4 +235,12 @@
     public void update(Chunk chunk) {
         storage.updateChunk(chunk);
     }
+
+    void setID(BackendID backendID) {
+        this.id = backendID;
+    }
+    
+    public BackendID getID() {
+        return id;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/backend/BackendID.java	Thu Mar 29 23:54:17 2012 +0200
@@ -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.backend;
+
+public class BackendID {
+
+    private String simpleName;
+    private String className;
+    
+    public BackendID(String simpleName, String className) {
+        this.simpleName = simpleName;
+        this.className = className;
+    }
+    
+    public String getSimpleName() {
+        return simpleName;
+    }
+    
+    public String getClassName() {
+        return className;
+    }
+    
+    @Override
+    public String toString() {
+        return simpleName + " = " + className;
+    }
+}
--- a/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Thu Mar 29 23:54:17 2012 +0200
@@ -33,18 +33,18 @@
  * 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.backend;
 
 import java.lang.reflect.Constructor;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.backend.system.SystemBackend;
-import com.redhat.thermostat.common.config.StartupConfiguration;
+import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
 import com.redhat.thermostat.common.storage.Storage;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
@@ -58,33 +58,30 @@
 
     private final Map<String, Backend> registeredBackends;
 
-    public BackendRegistry(StartupConfiguration config, Storage storage) throws BackendLoadException {
+    public BackendRegistry(AgentStartupConfiguration config, Storage storage) throws BackendLoadException {
+        
         registeredBackends = new HashMap<String, Backend>();
-
-        /*
-         * Configure the always-on backends
-         */
-        Backend systemBackend = new SystemBackend();
-        logger.log(Level.FINE, "Initializing backend: \"" + systemBackend.getClass().getCanonicalName() + "\"");
-        systemBackend.setInitialConfiguration(config.getStartupBackendConfigMap(systemBackend.getName()));
-        systemBackend.setStorage(storage);
-        register(systemBackend);
-
+        
+        List<BackendID> backends = config.getBackends();
+        
         /*
          * Configure the dynamic/custom backends
          */
-        for (String backendClassName : config.getStartupBackendClassNames()) {
-            logger.log(Level.FINE, "Initializing backend: \"" + backendClassName + "\"");
+        for (BackendID backendID : backends) {
+            logger.log(Level.FINE, "Initializing backend: \"" + backendID.getClassName() + "\"");
             Backend backend = null;
             try {
-                Class<? > c = Class.forName(backendClassName);
+                Class<? > c = Class.forName(backendID.getClassName());
                 Class<? extends Backend> narrowed = c.asSubclass(Backend.class);
                 Constructor<? extends Backend> backendConstructor = narrowed.getConstructor();
                 backend = backendConstructor.newInstance();
-                backend.setInitialConfiguration(config.getStartupBackendConfigMap(backend.getName()));
+                
+                backend.setID(backendID);
+                
+                backend.setInitialConfiguration(BackendRegistryUtils.retrieveBackendConfigs(backend.getName()));
                 backend.setStorage(storage);
             } catch (Exception e) {
-                throw new BackendLoadException("Could not instantiate configured backend class: " + backendClassName, e);
+                throw new BackendLoadException("Could not instantiate configured backend class: " + backendID.getClassName(), e);
             }
             register(backend);
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistryUtils.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,82 @@
+/*
+ * 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.backend;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import com.redhat.thermostat.common.config.ConfigUtils;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+
+class BackendRegistryUtils {
+
+    public static Map<String, String> retrieveBackendConfigs(String name) throws InvalidConfigurationException {
+        
+        // reads the backend
+        File backend = new File(ConfigUtils.getBackendsBaseDirectory(), name);
+        backend = new File(backend, BackendsProperties.PROPERTY_FILE);
+        if (!backend.isFile() || !backend.canRead()) {
+            throw new InvalidConfigurationException("invalid backend configuration file: " + backend);
+        }
+        
+        Properties props = new Properties();
+        try {
+            props.load(new FileInputStream(backend));
+        } catch (IOException e) {
+            throw new InvalidConfigurationException("invalid backend configuration file", e);
+        }
+        
+        return getStartupBackendConfigMap(props);
+    }
+
+    private static Map<String, String> getStartupBackendConfigMap(Properties props) {
+
+        Map<String, String> configMap = new HashMap<>();
+        for (Entry<Object, Object> e : props.entrySet()) {
+            String key = (String) e.getKey();
+            String value = (String) e.getValue();
+            
+            configMap.put(key, value);
+        }
+        return configMap;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/backend/BackendsProperties.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,50 @@
+/*
+ * 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.backend;
+
+/**
+ * Properties that any Backend needs to have, at minimum.
+ */
+public enum BackendsProperties {
+    
+    DESCRIPTION,
+    VENDOR,
+    VERSION,
+    BACKEND_CLASS;
+    
+    public static final String PROPERTY_FILE = "backend.properties";
+}
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Thu Mar 29 23:54:17 2012 +0200
@@ -55,6 +55,8 @@
 import com.redhat.thermostat.agent.JvmStatusListener;
 import com.redhat.thermostat.agent.JvmStatusNotifier;
 import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.SystemClock;
 import com.redhat.thermostat.common.dao.CpuStatConverter;
 import com.redhat.thermostat.common.dao.CpuStatDAO;
 import com.redhat.thermostat.common.dao.HostInfoConverter;
@@ -75,11 +77,6 @@
 
 public class SystemBackend extends Backend implements JvmStatusNotifier, JvmStatusListener {
 
-    private static final String NAME = "system";
-    private static final String DESCRIPTION = "gathers basic information from the system";
-    private static final String VENDOR = "thermostat project";
-    private static final String VERSION = "0.01";
-
     private static final Logger logger = LoggingUtils.getLogger(SystemBackend.class);
 
     private long procCheckInterval = 1000; // TODO make this configurable.
@@ -92,8 +89,16 @@
 
     private Set<Integer> pidsToMonitor = new CopyOnWriteArraySet<Integer>();
 
+    private final VmCpuStatBuilder vmCpuBuilder;
+
     private static List<Category> categories = new ArrayList<Category>();
 
+    public SystemBackend() {
+        Clock clock = new SystemClock();
+        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource());
+        long ticksPerSecond = SysConf.getClockTicksPerSecond();
+        vmCpuBuilder = new VmCpuStatBuilder(clock, ticksPerSecond, builder);
+    }
 
     static {
         // Set up categories that will later be registered.
@@ -109,26 +114,6 @@
     }
 
     @Override
-    public String getName() {
-        return NAME;
-    }
-
-    @Override
-    public String getDescription() {
-        return DESCRIPTION;
-    }
-
-    @Override
-    public String getVendor() {
-        return VENDOR;
-    }
-
-    @Override
-    public String getVersion() {
-        return VERSION;
-    }
-
-    @Override
     public synchronized boolean activate() {
         if (timer != null) {
             return true;
@@ -153,8 +138,11 @@
                 store(new MemoryStatConverter().memoryStatToChunk(new MemoryStatBuilder(dataSource).build()));
 
                 for (Integer pid : pidsToMonitor) {
-                    new VmCpuStatBuilder();
-                    store(new VmCpuStatConverter().vmCpuStatToChunk(VmCpuStatBuilder.build(pid)));
+                    if (vmCpuBuilder.knowsAbout(pid)) {
+                        store(new VmCpuStatConverter().vmCpuStatToChunk(vmCpuBuilder.build(pid)));
+                    } else {
+                        vmCpuBuilder.learnAbout(pid);
+                    }
                 }
             }
         }, 0, procCheckInterval);
@@ -234,5 +222,6 @@
     @Override
     public void jvmStopped(int vmId) {
         pidsToMonitor.remove(vmId);
+        vmCpuBuilder.forgetAbout(vmId);
     }
 }
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java	Thu Mar 29 23:54:17 2012 +0200
@@ -39,16 +39,25 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import com.redhat.thermostat.common.Clock;
 import com.redhat.thermostat.common.model.VmCpuStat;
 
 public class VmCpuStatBuilder {
 
     // pid -> ticks
-    private static Map<Integer, Long> lastProcessTicks = new HashMap<Integer, Long>();
+    private final Map<Integer, Long> lastProcessTicks = new HashMap<Integer, Long>();
     // pid -> last time the ticks were updated
-    private static Map<Integer, Long> lastProcessTickTime = new HashMap<Integer, Long>();
+    private final Map<Integer, Long> lastProcessTickTime = new HashMap<Integer, Long>();
+
+    private final Clock clock;
+    private final long ticksPerSecond;
+    private final ProcessStatusInfoBuilder statusBuilder;
 
-    private static long clockTicksPerSecond = SysConf.getClockTicksPerSecond();
+    public VmCpuStatBuilder(Clock clock, long ticksPerSecond, ProcessStatusInfoBuilder statusBuilder) {
+        this.clock = clock;
+        this.ticksPerSecond = ticksPerSecond;
+        this.statusBuilder = statusBuilder;
+    }
 
     /**
      * To build the stat, this method needs to be called repeatedly. The first
@@ -58,26 +67,43 @@
      * @param pid
      * @return
      */
-    public static synchronized VmCpuStat build(Integer pid) {
+    public synchronized VmCpuStat build(Integer pid) {
+        if (!lastProcessTicks.containsKey(pid) || !lastProcessTickTime.containsKey(pid)) {
+            throw new IllegalArgumentException("unknown pid");
+        }
 
-        ProcDataSource dataSource = new ProcDataSource();
-        ProcessStatusInfo info = new ProcessStatusInfoBuilder(dataSource).build(pid);
-        long miliTime = System.currentTimeMillis();
-        long time = System.nanoTime();
+        ProcessStatusInfo info = statusBuilder.build(pid);
+        long miliTime = clock.getRealTimeMillis();
+        long time = clock.getMonotonicTimeNanos();
         long programTicks = (info.getKernelTime() + info.getUserTime());
         double cpuLoad = 0.0;
 
-        if (lastProcessTicks.containsKey(pid)) {
-            double timeDelta = (time - lastProcessTickTime.get(pid)) * 1E-9;
-            long programTicksDelta = programTicks - lastProcessTicks.get(pid);
-            cpuLoad = programTicksDelta * (100.0 / timeDelta / clockTicksPerSecond);
-        }
+        double timeDelta = (time - lastProcessTickTime.get(pid)) * 1E-9;
+        long programTicksDelta = programTicks - lastProcessTicks.get(pid);
+        // 100 as in 100 percent.
+        cpuLoad = programTicksDelta * (100.0 / timeDelta / ticksPerSecond);
 
         lastProcessTicks.put(pid, programTicks);
         lastProcessTickTime.put(pid, time);
 
-
         return new VmCpuStat(miliTime, pid, cpuLoad);
     }
 
+    public synchronized boolean knowsAbout(int pid) {
+        return (lastProcessTickTime.containsKey(pid) && lastProcessTicks.containsKey(pid));
+    }
+
+    public synchronized void learnAbout(int pid) {
+        long time = clock.getMonotonicTimeNanos();
+        ProcessStatusInfo info = statusBuilder.build(pid);
+
+        lastProcessTickTime.put(pid, time);
+        lastProcessTicks.put(pid, info.getUserTime()+ info.getKernelTime());
+    }
+
+    public synchronized void forgetAbout(int pid) {
+        lastProcessTicks.remove(pid);
+        lastProcessTickTime.remove(pid);
+    }
+
 }
--- a/agent/src/test/java/com/redhat/thermostat/TestUtils.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/TestUtils.java	Thu Mar 29 23:54:17 2012 +0200
@@ -36,7 +36,19 @@
 
 package com.redhat.thermostat;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.lang.management.ManagementFactory;
+import java.util.Properties;
+import java.util.Random;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+
+import com.redhat.thermostat.agent.config.AgentProperties;
+import com.redhat.thermostat.backend.BackendsProperties;
 
 public class TestUtils {
 
@@ -49,5 +61,47 @@
     public static boolean isLinux() {
         return (System.getProperty("os.name").toLowerCase().contains("linux"));
     }
+        
+    public static String setupAgentConfigs() throws IOException {
+        // need to create dummy config files for the tests
+        Random random = new Random();
 
+        String tmpDir = System.getProperty("java.io.tmpdir") + File.separatorChar +
+                Math.abs(random.nextInt()) + File.separatorChar;
+
+        System.setProperty("THERMOSTAT_HOME", tmpDir);
+        File agent = new File(tmpDir, "agent");
+        agent.mkdirs();
+
+        File tmpConfigs = new File(agent, "agent.properties");
+
+        new File(agent, "run").mkdirs();
+        new File(agent, "logs").mkdirs();
+
+        File backends = new File(tmpDir, "backends");
+        File system = new File(backends, "system");
+        system.mkdirs();
+        
+        Properties props = new Properties();            
+
+        props.setProperty(AgentProperties.BACKENDS.name(), "system");
+        props.setProperty(AgentProperties.LOG_LEVEL.name(), "WARNING");
+
+        props.store(new FileOutputStream(tmpConfigs), "thermostat agent test properties");
+
+        // now write the configs for the backends
+        tmpConfigs = new File(system, "backend.properties");
+        props = new Properties();
+        props.setProperty(BackendsProperties.BACKEND_CLASS.name(),
+                          "com.redhat.thermostat.backend.system.SystemBackend");
+        props.setProperty(BackendsProperties.DESCRIPTION.name(),
+                          "fluff backend for tests");
+        props.setProperty(BackendsProperties.VENDOR.name(),
+                          "Red Hat, Inc.");
+        props.setProperty(BackendsProperties.VERSION.name(),
+                          "1.0");
+        props.store(new FileOutputStream(tmpConfigs), "thermostat system backend properties");
+        
+        return tmpDir;
+    }
 }
--- a/agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -48,9 +48,9 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
+import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
-import com.redhat.thermostat.common.config.StartupConfiguration;
 import com.redhat.thermostat.common.storage.AgentInformation;
 import com.redhat.thermostat.common.storage.BackendInformation;
 import com.redhat.thermostat.common.storage.Storage;
@@ -60,7 +60,7 @@
     @Test
     public void testStartAgent() throws Exception {
         // Setup class under test and test data (config, backendRegistry).
-        StartupConfiguration config = mock(StartupConfiguration.class);
+        AgentStartupConfiguration config = mock(AgentStartupConfiguration.class);
         when(config.getStartTime()).thenReturn(123L);
 
         Storage storage = mock(Storage.class);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/agent/config/AgentConfigsUtilsTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,69 @@
+/*
+ * 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.config;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.logging.Level;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.TestUtils;
+import com.redhat.thermostat.backend.BackendID;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+
+public class AgentConfigsUtilsTest {
+    
+    @Before
+    public void setUp() throws IOException, InvalidConfigurationException {
+        TestUtils.setupAgentConfigs();
+    }
+    
+    @Test
+    public void test() throws InvalidConfigurationException {
+        AgentStartupConfiguration config = AgentConfigsUtils.createAgentConfigs();        
+        List<BackendID> backends = config.getBackends();
+        
+        // the test property only define the system backend so far
+        Assert.assertEquals(1, backends.size());
+        Assert.assertEquals("system", backends.get(0).getSimpleName());
+        
+        Assert.assertEquals(Level.WARNING, config.getLogLevel());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/agent/config/AgentOptionParserTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,106 @@
+/*
+ * 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.config;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.logging.Level;
+
+import junit.framework.Assert;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.redhat.thermostat.TestUtils;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+
+public class AgentOptionParserTest {
+    
+    private static File tmpFile;
+    
+    @BeforeClass
+    public static void setup() throws IOException {
+        tmpFile = new File(TestUtils.setupAgentConfigs());
+    }
+    
+    @AfterClass
+    public static void shutdown() {
+        tmpFile.delete();
+    }
+    
+    @Test
+    public void testConfigs1() throws IOException, InvalidConfigurationException {
+        
+        List<String> args = new ArrayList<>();
+        args.add("--logLevel");
+        args.add("ALL");
+        args.add("--dbUrl");
+        args.add("testURL");
+        args.add("--debug");
+        
+        AgentStartupConfiguration configs = new AgentStartupConfiguration();
+        AgentOptionParser parser = new AgentOptionParser(configs, args);
+        parser.parse();
+        
+        Assert.assertEquals("testURL", configs.getDBConnectionString());
+        Assert.assertEquals(Level.ALL, configs.getLogLevel());
+        Assert.assertTrue(configs.isDebugConsole());
+    }
+    
+    @Test
+    public void testConfigs2() throws IOException, InvalidConfigurationException {
+        
+        List<String> args = new ArrayList<>();
+        args.add("--logLevel");
+        args.add("FINE");
+        args.add("--dbUrl");
+        args.add("testURL2");
+        
+        AgentStartupConfiguration configs = new AgentStartupConfiguration();
+        AgentOptionParser parser = new AgentOptionParser(configs, args);
+        parser.parse();
+        
+        Assert.assertEquals("testURL2", configs.getDBConnectionString());
+        Assert.assertEquals(Level.FINE, configs.getLogLevel());
+        Assert.assertFalse(configs.isDebugConsole());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/backend/BackendRegistryTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,76 @@
+/*
+ * 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.backend;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.TestUtils;
+import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
+import com.redhat.thermostat.backend.system.SystemBackend;
+import com.redhat.thermostat.common.storage.Storage;
+
+public class BackendRegistryTest {
+
+    @Before
+    public void setUp() throws IOException {
+        TestUtils.setupAgentConfigs();
+    }
+    
+    @Test
+    public void test() throws BackendLoadException {
+        
+        List<BackendID> backends = new ArrayList<>();
+        backends.add(new BackendID("system", SystemBackend.class.getCanonicalName()));
+        
+        Storage storage = mock(Storage.class);        
+        AgentStartupConfiguration config = mock(AgentStartupConfiguration.class);
+        when(config.getBackends()).thenReturn(backends);
+        
+        BackendRegistry registry = new BackendRegistry(config, storage);
+        Assert.assertEquals(1, registry.getAll().size());
+        Assert.assertNotNull(registry.getByName("system"));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/backend/BackendRegistryUtilsTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -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.backend;
+
+import java.io.IOException;
+import java.util.Map;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.TestUtils;
+import com.redhat.thermostat.backend.system.SystemBackend;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+
+public class BackendRegistryUtilsTest {
+    
+    @Before
+    public void setUp() throws IOException {
+        TestUtils.setupAgentConfigs();
+    }
+    
+    @Test
+    public void test() throws InvalidConfigurationException {
+        Map<String, String> backendProps = BackendRegistryUtils.retrieveBackendConfigs("system");
+        Assert.assertTrue(backendProps.containsKey(BackendsProperties.BACKEND_CLASS.name()));
+        
+        String className = backendProps.get(BackendsProperties.BACKEND_CLASS.name());
+        Assert.assertEquals(SystemBackend.class.getCanonicalName(), className);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/backend/system/VmCpuStatBuilderTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -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.backend.system;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.model.VmCpuStat;
+
+public class VmCpuStatBuilderTest {
+
+    @Test
+    public void testBuilderKnowsNothing() {
+        Clock clock = mock(Clock.class);
+        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
+        long ticksPerSecond = 0;
+        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, ticksPerSecond, statusBuilder);
+
+        assertFalse(builder.knowsAbout(0));
+        assertFalse(builder.knowsAbout(1));
+        assertFalse(builder.knowsAbout(Integer.MIN_VALUE));
+        assertFalse(builder.knowsAbout(Integer.MAX_VALUE));
+
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuilderThrowsOnBuildOfUnknownPid() {
+        Clock clock = mock(Clock.class);
+        long ticksPerSecond = 0;
+        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
+        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, ticksPerSecond, statusBuilder);
+        builder.build(0);
+    }
+
+    @Test
+    public void testSaneBuild() {
+        final int PID = 0;
+        final long USER_INITIAL_TICKS = 1;
+        final long KERNEL_INITIAL_TICKS = 1;
+
+        final long USER_LATER_TICKS = 10;
+        final long KERNEL_LATER_TICKS = 10;
+
+        final long CLOCK1 = 10000;
+        final long CLOCK2 = 20000;
+
+        final long TICKS_PER_SECOND = 100;
+
+        final double CPU_LOAD_PERCENT =
+                100.0
+                * ((USER_LATER_TICKS + KERNEL_LATER_TICKS) - (USER_INITIAL_TICKS + KERNEL_INITIAL_TICKS))
+                / TICKS_PER_SECOND
+                / ((CLOCK2 - CLOCK1) * 1E-3 /* millis to seconds */);
+
+        final ProcessStatusInfo initialInfo = new ProcessStatusInfo(PID, USER_INITIAL_TICKS, KERNEL_INITIAL_TICKS);
+        final ProcessStatusInfo laterInfo = new ProcessStatusInfo(PID, USER_LATER_TICKS, KERNEL_LATER_TICKS);
+
+        Clock clock = mock(Clock.class);
+        when(clock.getRealTimeMillis()).thenReturn(CLOCK2);
+        when(clock.getMonotonicTimeNanos()).thenReturn((long) (CLOCK1 * 1E6)).thenReturn((long) (CLOCK2 * 1E6));
+
+        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
+        when(statusBuilder.build(any(Integer.class))).thenReturn(initialInfo).thenReturn(laterInfo).thenReturn(null);
+
+        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, TICKS_PER_SECOND, statusBuilder);
+
+        builder.learnAbout(PID);
+        VmCpuStat stat = builder.build(PID);
+
+        assertNotNull(stat);
+        assertEquals(PID, stat.getVmId());
+        assertEquals(CLOCK2, stat.getTimeStamp());
+        assertEquals(CPU_LOAD_PERCENT, stat.getCpuLoad(), 0.0001);
+    }
+
+}
--- a/client/src/main/java/com/redhat/thermostat/client/Main.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/Main.java	Thu Mar 29 23:54:17 2012 +0200
@@ -48,6 +48,7 @@
 import javax.swing.SwingUtilities;
 
 import com.redhat.thermostat.client.appctx.ApplicationContext;
+import com.redhat.thermostat.client.config.ConnectionConfiguration;
 import com.redhat.thermostat.client.locale.LocaleResources;
 import com.redhat.thermostat.client.ui.ConnectionSelectionDialog;
 import com.redhat.thermostat.client.ui.LayoutDebugHelper;
@@ -82,15 +83,7 @@
             System.exit(-1);
         }
 
-        StartupConfiguration config = null;
-        try {
-            config = new StartupConfiguration(System.currentTimeMillis(), args);
-        } catch (LaunchException le) {
-            logger.log(Level.SEVERE,
-                    "Unable to instantiate startup configuration.",
-                    le);
-            System.exit(Constants.EXIT_CONFIGURATION_ERROR);
-        }
+        StartupConfiguration config = new ConnectionConfiguration();
         
         ConnectionProvider connProv = new MongoConnectionProvider(config);
         DAOFactory daoFactory = new MongoDAOFactory(connProv);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/config/ConnectionConfiguration.java	Thu Mar 29 23:54:17 2012 +0200
@@ -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.client.config;
+
+import com.redhat.thermostat.common.config.StartupConfiguration;
+
+public class ConnectionConfiguration implements StartupConfiguration {
+
+    @Override
+    public String getDBConnectionString() {
+        // TODO: this needs to be read from preferences or from the agent
+        // configuration
+        return "mongodb://127.0.0.1:27518";
+    }
+}
--- a/common/pom.xml	Thu Mar 29 23:52:18 2012 +0200
+++ b/common/pom.xml	Thu Mar 29 23:54:17 2012 +0200
@@ -101,6 +101,11 @@
     	<artifactId>easymock</artifactId>
     	<version>3.1</version>
     </dependency>
+    <dependency>
+    	<groupId>net.sf.jopt-simple</groupId>
+    	<artifactId>jopt-simple</artifactId>
+    	<version>4.3</version>
+    </dependency>
   </dependencies>
 
   <properties>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/Clock.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common;
+
+public interface Clock {
+
+    /**
+     * Returns the current real time in milliseconds (measured since the UNIX epoch).
+     */
+    long getRealTimeMillis();
+
+    /**
+     * Returns a time value corresponding to a monotonic clock measuring time
+     * in nanoseconds since some unspecified epoch.
+     */
+    long getMonotonicTimeNanos();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/SystemClock.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common;
+
+public class SystemClock implements Clock {
+
+    @Override
+    public long getRealTimeMillis() {
+        return System.currentTimeMillis();
+    }
+
+    @Override
+    public long getMonotonicTimeNanos() {
+        return System.nanoTime();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/config/ConfigUtils.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.config;
+
+import java.io.File;
+
+public class ConfigUtils {
+
+    public static String getThermostatHome() throws InvalidConfigurationException {
+        // allow this to be specified also as a property, especially for
+        // tests, this overrides the env setting
+        String home = System.getProperty("THERMOSTAT_HOME");
+        if (home == null) {
+            home = System.getenv("THERMOSTAT_HOME");
+        }
+        
+        if (home == null) {
+            throw new InvalidConfigurationException("THERMOSTAT_HOME not defined...");
+        }
+        return home;
+    }
+    
+    public static File getBackendsBaseDirectory() throws InvalidConfigurationException {
+        String loc = getThermostatHome() + File.separatorChar + "backends";
+        File file = new File(loc);
+        return file;
+    }
+    
+    public static File getStorageBaseDirectory() throws InvalidConfigurationException {
+        String loc = getThermostatHome() + File.separatorChar + "storage";
+        File file = new File(loc);
+        return file;
+    }
+    
+    public static File getStorageDirectory() throws InvalidConfigurationException {
+        return new File(getStorageBaseDirectory(), "db");
+    }
+    
+    public static File getStorageConfigurationFile() throws InvalidConfigurationException {
+        return new File(getStorageBaseDirectory(), "db.properties");
+    }
+
+    public static File getStorageLogFile() throws InvalidConfigurationException {
+        File logDir = new File(getStorageBaseDirectory(), "logs");
+        File logFile = new File(logDir, "db.log");
+        
+        return logFile;
+    }
+
+    public static File getStoragePidFile() throws InvalidConfigurationException {
+        File logDir = new File(getStorageBaseDirectory(), "run");
+        File logFile = new File(logDir, "db.pid");
+        
+        return logFile;
+    }
+
+    public static File getBackendPropertyFile(String backendName)
+            throws InvalidConfigurationException
+    {
+        File backendsConfigDir = ConfigUtils.getBackendsBaseDirectory();
+        File backendConfig = new File(backendsConfigDir, backendName);
+        backendConfig = new File(backendConfig, "backend.properties");
+        if (!backendConfig.exists())
+            throw new InvalidConfigurationException("backends configuration " +
+                                                    "directory doesn't exist: " + 
+                                                    backendConfig);
+        return backendConfig;
+    }
+
+    public static File getAgentConfigurationFile() throws InvalidConfigurationException {
+
+        File agent = new File(getThermostatHome(), "agent");
+        return new File(agent, "agent.properties");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/config/InvalidConfigurationException.java	Thu Mar 29 23:54:17 2012 +0200
@@ -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.common.config;
+
+public class InvalidConfigurationException extends Exception {
+
+    public InvalidConfigurationException() {
+        super();
+    }
+    
+    public InvalidConfigurationException(String message) {
+        super(message);
+    }
+    
+    public InvalidConfigurationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    
+    public InvalidConfigurationException(Throwable cause) {
+        super(cause);
+    }
+}
--- a/common/src/main/java/com/redhat/thermostat/common/config/StartupConfiguration.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/config/StartupConfiguration.java	Thu Mar 29 23:54:17 2012 +0200
@@ -36,246 +36,7 @@
 
 package com.redhat.thermostat.common.config;
 
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.config.Defaults;
-import com.redhat.thermostat.common.Constants;
-import com.redhat.thermostat.common.LaunchException;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class StartupConfiguration {
-
-    private static Logger logger = LoggingUtils.getLogger(StartupConfiguration.class);
-
-    private final long startTimestamp;
-
-    private Properties props = new Properties();
-    private Level logLevel;
-    private boolean localMode;
-    private int mongodPort;
-    private String mongoScript;
-    private int mongosPort;
-    private String databaseURI;
-    private String completeDatabaseURI;
-    private Arguments arguments;
-
-    private String hostname;
-
-    public StartupConfiguration(long startTime, String[] args) throws LaunchException {
-
-        initFromDefaults();
-        initFromArguments(args);
-        initFromProperties();
-
-        try {
-            InetAddress addr = InetAddress.getLocalHost();
-            hostname = addr.getCanonicalHostName();
-        } catch (UnknownHostException e) {
-            logger.log(Level.FINE, "Error determining local hostname.");
-        }
-
-        startTimestamp = startTime;
-    }
-
-    private void initFromDefaults() {
-        logLevel = Defaults.LOGGING_LEVEL;
-        localMode = Defaults.LOCAL_MODE;
-        mongodPort = Defaults.MONGOD_PORT;
-        mongosPort = Defaults.MONGOS_PORT;
-        databaseURI = Defaults.DATABASE_URI;
-    }
-
-    private void initFromProperties() throws LaunchException {
-        if (props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT) != null) {
-            mongodPort = Integer.valueOf(props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT));
-        }
-        if (props.getProperty(Constants.AGENT_PROPERTY_MONGO_LAUNCH_SCRIPT) != null) {
-            mongoScript = props.getProperty(Constants.AGENT_PROPERTY_MONGO_LAUNCH_SCRIPT);
-            logger.finest("Mongo launch script at: " + mongoScript);
-        } else {
-            throw new LaunchException("No mongo launch script in properties.");
-        }
-        if (props.getProperty(Constants.AGENT_PROPERTY_MONGOS_PORT) != null) {
-            mongosPort = Integer.valueOf(props.getProperty(Constants.AGENT_PROPERTY_MONGOS_PORT));
-        }
-    }
-
-    private void initFromArguments(String[] args) throws LaunchException {
-        arguments = new Arguments(args);
-        if (arguments.isModeSpecified()) {
-            localMode = arguments.getLocalMode();
-        }
-        if (arguments.isLogLevelSpecified()) {
-            logLevel = arguments.getLogLevel();
-        }
-    }
-    
-    public Level getLogLevel() {
-        return logLevel;
-    }
-
-    public String getMongoLaunchScript() {
-        return mongoScript;
-    }
-
-    public synchronized String getDatabaseURIAsString() {
-        if (completeDatabaseURI == null) {
-            if (localMode) {
-                completeDatabaseURI = getDBUriString();
-            } else {
-                completeDatabaseURI = getClusterDBUriString();
-            }
-        }
-        return completeDatabaseURI;
-    }
-    
-    public String getDBUriString() {
-        return databaseURI + ":" + mongodPort;
-    }
-    
-    // FIXME: this needs to go away, it's an implementation detail
-    public String getClusterDBUriString() {
-        return databaseURI + ":" + mongosPort;
-    }    
-    
-
-    public String getHostname() {
-        return hostname;
-    }
+public interface StartupConfiguration {
 
-    public boolean getLocalMode() {
-        boolean local = false;
-        try {
-            local = arguments.getLocalMode();
-        } catch (IllegalStateException ise) {
-            local = false;
-        }
-        return local;
-    }
-
-    public List<String> getStartupBackendClassNames() {
-        String fullPropertyString = props.getProperty(Constants.AGENT_PROPERTY_BACKENDS);
-        if ((fullPropertyString == null) // Avoid NPE
-                || (fullPropertyString.length() == 0)) { /* If we do the split() on this empty string,
-                                                          * it will produce an array of size 1 containing
-                                                          * the empty string, which we do not want.
-                                                          */
-            return new ArrayList<String>();
-        } else {
-            return Arrays.asList(fullPropertyString.trim().split(","));
-        }
-    }
-
-    public Map<String, String> getStartupBackendConfigMap(String backendName) {
-        String prefix = backendName + ".";
-        Map<String, String> configMap = new HashMap<String, String>();
-        for (Entry<Object, Object> e : props.entrySet()) {
-            String key = (String) e.getKey();
-            if (key.startsWith(prefix)) {
-                String value = (String) e.getValue();
-                String mapKey = key.substring(prefix.length());
-                configMap.put(mapKey, value);
-            }
-        }
-        return configMap;
-    }
-
-    public long getStartTime() {
-        return startTimestamp;
-    }
-
-    /**
-     * Exposes the command line arguments in a more object-oriented style.
-     * <p>
-     * Please check that an option was specified (using the various is*Specified() methods) before using its value.
-     */
-    private class Arguments {
-        private final boolean localMode;
-        private final boolean modeSpecified;
-        private final Level logLevel;
-        private final boolean logLevelSpecified;
-
-        public Arguments(String[] args) throws LaunchException {
-            boolean local = false;
-            boolean explicitMode = false;
-            Level level = null;
-            boolean explicitLogLevel = false;
-            boolean noProps = true;
-            for (int index = 0; index < args.length; index++) {
-                if (args[index].equals(Constants.AGENT_ARGUMENT_LOGLEVEL)) {
-                    index++;
-                    if (index < args.length) {
-                        try {
-                            level = Level.parse(args[index].toUpperCase());
-                            explicitLogLevel = true;
-                        } catch (IllegalArgumentException iae) {
-                            throw new LaunchException("Invalid argument for " + Constants.AGENT_ARGUMENT_LOGLEVEL, iae);
-                        }
-                    } else {
-                        throw new LaunchException("Missing argument for " + Constants.AGENT_ARGUMENT_LOGLEVEL);
-                    }
-                } else if (args[index].equals(Constants.AGENT_ARGUMENT_LOCAL)) {
-                    explicitMode = true;
-                    local = true;
-                } else if (args[index].equals(Constants.AGENT_ARGUMENT_PROPERTIES)) {
-                    logger.finest("Properties argument specified.");
-                    index++;
-                    if (index < args.length) {
-                        String propFile = args[index];
-                        logger.finest("Properties file: " + propFile);
-                        try (Reader reader = new FileReader(propFile)) {
-                            props.load(reader);
-                        } catch (IOException ioe) {
-                            throw new LaunchException("Unable to read properties file at " + propFile);
-                        }
-                        noProps = false;
-                    } else {
-                        throw new LaunchException("Missing argument for " + Constants.AGENT_ARGUMENT_PROPERTIES);
-                    }
-                }
-            }
-            if (noProps) {
-                throw new LaunchException("Required properties file not specified.");
-            }
-            logLevel = level;
-            logLevelSpecified = explicitLogLevel;
-            localMode = local;
-            modeSpecified = explicitMode;
-        }
-
-        public boolean isModeSpecified() {
-            return modeSpecified;
-        }
-
-        public boolean getLocalMode() {
-            if (!isModeSpecified()) {
-                throw new IllegalStateException("local mode is not valid");
-            }
-            return localMode;
-        }
-
-        public boolean isLogLevelSpecified() {
-            return logLevelSpecified;
-        }
-
-        public Level getLogLevel() {
-            if (!isLogLevelSpecified()) {
-                throw new IllegalStateException("log level not explicitly specified");
-            }
-            return logLevel;
-        }
-    }
+    String getDBConnectionString();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/config/ThermostatOptionParser.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.config;
+
+public interface ThermostatOptionParser {
+    void parse() throws InvalidConfigurationException;
+    void displayHelp();
+}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java	Thu Mar 29 23:54:17 2012 +0200
@@ -88,16 +88,7 @@
     }
 
     private MongoURI getMongoURI() {
-        MongoURI uri = null;
-        switch (getType()) {
-            case LOCAL:
-                uri = new MongoURI(conf.getDBUriString());
-                break;
-            case REMOTE:
-                throw new NotImplementedException("No mongo URI implemented for REMOTE.");
-            case CLUSTER:
-                throw new NotImplementedException("No mongo URI implemented for CLUSTER.");
-        }
+        MongoURI uri = new MongoURI(conf.getDBConnectionString());
         return uri;
     }
 
--- a/common/src/main/java/com/redhat/thermostat/common/model/VmCpuStat.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/model/VmCpuStat.java	Thu Mar 29 23:54:17 2012 +0200
@@ -56,6 +56,9 @@
         return vmId;
     }
 
+    /**
+     * The cpu load in percent (as in 100.0 for 100%)
+     */
     public double getCpuLoad() {
         return cpuLoad;
     }
--- a/common/src/main/java/com/redhat/thermostat/common/utils/LoggedExternalProcess.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/utils/LoggedExternalProcess.java	Thu Mar 29 23:54:17 2012 +0200
@@ -40,6 +40,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.Arrays;
+import java.util.List;
 import java.util.logging.Logger;
 
 public class LoggedExternalProcess extends Thread {
@@ -48,6 +49,10 @@
     private BufferedReader reader;
     private String[] commands;
 
+    public LoggedExternalProcess(List<String> commands) {
+        this(commands.toArray(new String[0]));
+    }
+    
     public LoggedExternalProcess(String[] commands) {
         this.commands = Arrays.copyOf(commands, commands.length);
         setDaemon(true);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/tools/Application.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,52 @@
+/*
+ * 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.tools;
+
+import java.util.List;
+
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+
+public interface Application {
+    
+    void parseArguments(List<String> args) throws InvalidConfigurationException;
+    void run();
+    void printHelp();
+    ActionNotifier<ApplicationState> getNotifier();
+    StartupConfiguration getConfiguration();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/tools/ApplicationState.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,50 @@
+/*
+ * 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.tools;
+
+/**
+ * Denotes the condition this application is in.
+ */
+public enum ApplicationState {
+
+    NONE,
+    SUCCESS,
+    FAIL,
+    HELP,
+    START,
+    STOP;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/tools/BasicApplication.java	Thu Mar 29 23:54:17 2012 +0200
@@ -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.tools;
+
+import com.redhat.thermostat.common.ActionNotifier;
+
+/**
+ * Common base class for all daemon and application
+ */
+public abstract class BasicApplication implements Application {
+    
+    private ActionNotifier<ApplicationState> notifier;
+    
+    public BasicApplication() {
+        this.notifier = new ActionNotifier<>(this);
+    }
+  
+    protected void notifyFail() {
+        notifier.fireAction(ApplicationState.FAIL);
+    }
+    
+    protected void notifySuccess() {
+        notifier.fireAction(ApplicationState.SUCCESS);
+    }
+    
+    @Override
+    public ActionNotifier<ApplicationState> getNotifier() {
+        return notifier;
+    }
+    
+    @Override
+    public void printHelp() {}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/test/java/com/redhat/thermostat/common/TestUtils.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.Properties;
+import java.util.Random;
+
+public class TestUtils {
+
+    public static int getProcessId() {
+        String name = ManagementFactory.getRuntimeMXBean().getName();
+        String pidPart = name.split("@")[0];
+        return Integer.parseInt(pidPart);
+    }
+
+    public static boolean isLinux() {
+        return (System.getProperty("os.name").toLowerCase().contains("linux"));
+    }
+        
+    public static String setupAgentConfigs() throws IOException {
+        // need to create dummy config files for the tests
+        Random random = new Random();
+
+        String tmpDir = System.getProperty("java.io.tmpdir") + File.separatorChar +
+                Math.abs(random.nextInt()) + File.separatorChar;
+
+        System.setProperty("THERMOSTAT_HOME", tmpDir);
+        File agent = new File(tmpDir, "agent");
+        agent.mkdirs();
+
+        File tmpConfigs = new File(agent, "agent.properties");
+
+        new File(agent, "run").mkdirs();
+        new File(agent, "logs").mkdirs();
+
+        File backends = new File(tmpDir, "backends");
+        File system = new File(backends, "system");
+        system.mkdirs();
+        
+        Properties props = new Properties();            
+
+        props.setProperty("BACKENDS", "system");
+        props.setProperty("LOG_LEVEL", "WARNING");
+
+        props.store(new FileOutputStream(tmpConfigs), "thermostat agent test properties");
+
+        // now write the configs for the backends
+        tmpConfigs = new File(system, "backend.properties");
+        props = new Properties();
+        props.setProperty("BACKEND_CLASS",
+                          "com.redhat.thermostat.backend.system.SystemBackend");
+        props.setProperty("DESCRIPTION",
+                          "fluff backend for tests");
+        props.setProperty("VENDOR",
+                          "Red Hat, Inc.");
+        props.setProperty("VERSION",
+                          "1.0");
+        props.store(new FileOutputStream(tmpConfigs), "thermostat system backend properties");
+        
+        return tmpDir;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/test/java/com/redhat/thermostat/common/config/ConfigUtilsTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Random;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.TestUtils;
+
+public class ConfigUtilsTest {
+
+    private String oldLocation;
+    
+    @Before
+    public void setUp() throws IOException {
+        TestUtils.setupAgentConfigs();
+    }
+    
+    @Test
+    public void testLocations() throws InvalidConfigurationException, IOException {
+        String path = System.getProperty("THERMOSTAT_HOME");
+        char s = File.separatorChar;
+        
+        Assert.assertEquals(path, ConfigUtils.getThermostatHome());
+        
+        Assert.assertEquals(path + "backends", ConfigUtils.getBackendsBaseDirectory().getCanonicalPath());
+        Assert.assertEquals(path + "storage", ConfigUtils.getStorageBaseDirectory().getCanonicalPath());
+        Assert.assertEquals(path + "storage" + s + "db.properties",
+                            ConfigUtils.getStorageConfigurationFile().getCanonicalPath());
+        Assert.assertEquals(path + "storage" + s + "db",
+                ConfigUtils.getStorageDirectory().getCanonicalPath());
+        Assert.assertEquals(path + "storage" + s + "logs" + s + "db.log",
+                ConfigUtils.getStorageLogFile().getCanonicalPath());
+        Assert.assertEquals(path + "storage" + s + "run" + s + "db.pid",
+                ConfigUtils.getStoragePidFile().getCanonicalPath());
+        
+        Assert.assertEquals(path + "backends" + s + "system" + s + "backend.properties",
+                            ConfigUtils.getBackendPropertyFile("system").getCanonicalPath());
+        
+    }
+}
--- a/common/src/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java	Thu Mar 29 23:52:18 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -103,7 +103,7 @@
     public void testInvalidLocalConnection() throws InterruptedException {
         
         StartupConfiguration conf = mock(StartupConfiguration.class);
-        when(conf.getDBUriString()).thenReturn("fluff");
+        when(conf.getDBConnectionString()).thenReturn("fluff");
         ConnectionProvider connProv = new MongoConnectionProvider(conf);
         
         Connection connection = connProv.createConnection();
--- a/distribution/config/agent.properties	Thu Mar 29 23:52:18 2012 +0200
+++ b/distribution/config/agent.properties	Thu Mar 29 23:54:17 2012 +0200
@@ -1,43 +1,5 @@
-## Mongo storage configuration
-# To be used when agent is running with --local argument
-mongod_port=27518
-mongo_launch_script=@project.build.directory@/scripts/localmongo.sh
-# To be used when connecting on localhost without --local argument (cluster)
-mongos_port=27517
-### Next few settings are used only by the mongo launch script
-config_port=27519
-config_url=mongodb://127.0.0.1
-### End section specific to launch script
-### Properties needed by client
-agent_launch_script=@project.build.directory@/scripts/thermostat-agent
-### End properties needed by client
-#
 ## Backend Configuration
-# This must be a comma separated list naming the fully qualified class name for
-# each backend that should run
-backends=
-# Backends may also use their name as prefix for backend-specific configuration.
-# For example, if backend foo requires a property called bar, then a line
-# containing 'foo.bar=baz' should be included.  'foo' in this case should be
-# the human-friendly alias for the backend, ie the string returned by the
-# getName() method of the class listed in the backends string.
-#
-# For each backend, there may be a .active property specified.
-# ie: for backend 'foo' there should be a 'foo.active = bar' line.  'bar'
-# must be a comma separated list including one or more of:
-#   'new' - The backend should attempt to attach to any new java process that
-#           starts.  Existing processes should not be instrumented.
-#   'all' - The backend should attempt to attach to all existing java
-#           java processes currently running on the machine.
-#   '[lvmid]' - One or more Local Virtual Machine IDs may be specified.  These
-#           are equivalent to the Linux process id.  This allows for the case
-#           of specific existing java processes to be instrumented.
-#
-# Alternatively, you may specify 'none' and the backend will not begin collecting
-# from any VM, but will be available to clients who wish to activate it.
-#
-# If there is no .active property specified, then 'none' is implied.
-#
-## Sample backend configuration
-#sample-backend.active=none
-#sample-backend.myconfiguration=property
\ No newline at end of file
+# This must be a comma separated list naming the backend
+# specific backend configurations will be searched under the
+# $THERMOSTAT_HOME/backends/[backend_name.properties]
+BACKENDS=system
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/config/db.properties	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,4 @@
+LOCAL=27518
+CLUSTER=27517
+BIND=127.0.0.1
+URL=mongodb://127.0.0.1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/config/system/backend.properties	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,4 @@
+BACKEND_CLASS=com.redhat.thermostat.backend.system.SystemBackend
+DESCRIPTION=gathers basic information from the system
+VENDOR=thermostat project
+VERSION=0.01
--- a/distribution/pom.xml	Thu Mar 29 23:52:18 2012 +0200
+++ b/distribution/pom.xml	Thu Mar 29 23:54:17 2012 +0200
@@ -139,5 +139,10 @@
       <artifactId>thermostat-common</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+    	<groupId>com.redhat.thermostat</groupId>
+    	<artifactId>thermostat-tools</artifactId>
+    	<version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
--- a/distribution/scripts/thermostat-agent	Thu Mar 29 23:52:18 2012 +0200
+++ b/distribution/scripts/thermostat-agent	Thu Mar 29 23:54:17 2012 +0200
@@ -39,60 +39,20 @@
 # Some necessary variables.
 JAVA_DIR="@java.dir@"
 JAVA="@java.home@/bin/java"
-JCOMMON_JAR="${JAVA_DIR}/jcommon.jar"
-MONGO_JAR="${JAVA_DIR}/mongo.jar"
-BSON_JAR="${JAVA_DIR}/bson.jar"
+
+THERMOSTAT_LIBS="${THERMOSTAT_HOME}/libs"
+
+JCOMMON_JAR="${THERMOSTAT_LIBS}/jcommon.jar"
+MONGO_JAR="${THERMOSTAT_LIBS}/mongo.jar"
+BSON_JAR="${THERMOSTAT_LIBS}/bson.jar"
+JOPT_JAR="${THERMOSTAT_LIBS}/jopt-simple-4.3.jar"
 TOOLS_JAR="@java.home@/../lib/tools.jar"
-AGENT_CLASSPATH="${JCOMMON_JAR}:${MONGO_JAR}:${BSON_JAR}:${TOOLS_JAR}"
-# Find the directory where thermostat is installed.
-# Note this will not work if there are symlinks to resolve that
-# are not full paths.
-SOURCE="${BASH_SOURCE[0]}"
-while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
-THERM_DIR=`dirname "$( cd -P "$( dirname "$SOURCE" )" && pwd )"`
-# Some other necessary variables.
-AGENT_CLASSPATH="${AGENT_CLASSPATH}:${THERM_DIR}/lib/thermostat-agent-@project.version@.jar:${THERM_DIR}/lib/thermostat-common-@project.version@.jar"
-AGENT_MAIN="com.redhat.thermostat.agent.Main"
-
-AGENT_ARGS="--properties ${THERM_DIR}/config/agent.properties"
-
-function usage {
-  echo "Usage:"
-  echo "  thermostat-agent <--local|--remote|--cluster>"
-}
-
-function run_agent {
-  ${JAVA} -cp ${AGENT_CLASSPATH} ${AGENT_MAIN} ${AGENT_ARGS}
-}
 
-function run_local {
-  AGENT_ARGS="${AGENT_ARGS} --local"
-  run_agent
-  exit $?
-}
+SERVICE_CLASSPATH="${JCOMMON_JAR}:${MONGO_JAR}:${BSON_JAR}:${TOOLS_JAR}:${JOPT_JAR}"
 
-function run_remote {
-  echo "Remote agent not yet supported."
-  exit -1
-}
-
-function run_cluster {
-  echo "Cluster agent not yet supported."
-  exit -1
-}
+THERM_DIR=${THERMOSTAT_LIBS}
 
-if [ $# != 1 ]; then
-  usage
-  exit -1
-fi
+SERVICE_CLASSPATH="${SERVICE_CLASSPATH}:${THERM_DIR}/thermostat-agent-@project.version@.jar:${THERM_DIR}/thermostat-common-@project.version@.jar:${THERM_DIR}/thermostat-tools-@project.version@.jar"
+SERVICE_MAIN="com.redhat.thermostat.agent.AgentApplication"
 
-if [ $1 = "--local" ]; then
-  run_local
-elif [ $1 = "--remote" ]; then
-  run_remote
-elif [ $1 = "--cluster" ]; then
-  run_cluster
-else
-  usage
-  exit -1
-fi
+${JAVA} -cp ${SERVICE_CLASSPATH} ${SERVICE_MAIN} $@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/scripts/thermostat-db	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# 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.
+#
+#####################################################################
+#
+# Some necessary variables.
+JAVA_DIR="@java.dir@"
+JAVA="@java.home@/bin/java"
+
+THERMOSTAT_LIBS="${THERMOSTAT_HOME}/libs"
+
+JCOMMON_JAR="${THERMOSTAT_LIBS}/jcommon.jar"
+MONGO_JAR="${THERMOSTAT_LIBS}/mongo.jar"
+BSON_JAR="${THERMOSTAT_LIBS}/bson.jar"
+JOPT_JAR="${THERMOSTAT_LIBS}/jopt-simple-4.3.jar"
+TOOLS_JAR="@java.home@/../lib/tools.jar"
+
+SERVICE_CLASSPATH="${JCOMMON_JAR}:${MONGO_JAR}:${BSON_JAR}:${TOOLS_JAR}:${JOPT_JAR}"
+
+THERM_DIR=${THERMOSTAT_LIBS}
+
+SERVICE_CLASSPATH="${SERVICE_CLASSPATH}:${THERM_DIR}/thermostat-agent-@project.version@.jar:${THERM_DIR}/thermostat-common-@project.version@.jar:${THERM_DIR}/thermostat-tools-@project.version@.jar"
+SERVICE_MAIN="com.redhat.thermostat.tools.db.DBService"
+
+${JAVA} -cp ${SERVICE_CLASSPATH} ${SERVICE_MAIN} $@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/scripts/thermostat-init-layout	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,76 @@
+#!/bin/bash
+#
+# 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.
+#
+#####################################################################
+#
+# Some necessary variables.
+if [ "$THERMOSTAT_HOME" = "" ]; then
+    echo "THERMOSTAT_HOME not set!"
+
+else
+    THERMOSTAT_STORAGE="${THERMOSTAT_HOME}/storage/db"
+    THERMOSTAT_LOG="${THERMOSTAT_HOME}/storage/log"
+    THERMOSTAT_PID="${THERMOSTAT_HOME}/storage/run"
+    THERMOSTAT_BACKEND="${THERMOSTAT_HOME}/backends/system"
+    THERMOSTAT_BIN="${THERMOSTAT_HOME}/bin"
+    THERMOSTAT_AGENT_LOG="${THERMOSTAT_HOME}/agent/logs"
+    THERMOSTAT_AGENT_RUN="${THERMOSTAT_HOME}/agent/run"
+    THERMOSTAT_LIBS="${THERMOSTAT_HOME}/libs"
+
+    echo "creating $THERMOSTAT_STORAGE"
+    mkdir -p $THERMOSTAT_STORAGE
+    
+    echo "creating $THERMOSTAT_LOG"
+    mkdir -p $THERMOSTAT_LOG
+    
+    echo "creating $THERMOSTAT_PID"
+    mkdir -p $THERMOSTAT_PID
+    
+    echo "creating $THERMOSTAT_BACKEND"
+    mkdir -p $THERMOSTAT_BACKEND
+    
+    echo "creating $THERMOSTAT_BIN"
+    mkdir -p $THERMOSTAT_BIN
+    
+    echo "creating $THERMOSTAT_AGENT_LOG"
+    mkdir -p $THERMOSTAT_AGENT_LOG
+    
+    echo "creating $THERMOSTAT_AGENT_RUN"
+    mkdir -p $THERMOSTAT_AGENT_RUN
+    
+    echo "creating $THERMOSTAT_LIBS"
+    mkdir -p $THERMOSTAT_LIBS
+fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/scripts/thermostat-service	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# 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.
+#
+#####################################################################
+#
+# Some necessary variables.
+JAVA_DIR="@java.dir@"
+JAVA="@java.home@/bin/java"
+
+THERMOSTAT_LIBS="${THERMOSTAT_HOME}/libs"
+
+JCOMMON_JAR="${THERMOSTAT_LIBS}/jcommon.jar"
+MONGO_JAR="${THERMOSTAT_LIBS}/mongo.jar"
+BSON_JAR="${THERMOSTAT_LIBS}/bson.jar"
+JOPT_JAR="${THERMOSTAT_LIBS}/jopt-simple-4.3.jar"
+TOOLS_JAR="@java.home@/../lib/tools.jar"
+
+SERVICE_CLASSPATH="${JCOMMON_JAR}:${MONGO_JAR}:${BSON_JAR}:${TOOLS_JAR}:${JOPT_JAR}"
+
+THERM_DIR=${THERMOSTAT_LIBS}
+
+SERVICE_CLASSPATH="${SERVICE_CLASSPATH}:${THERM_DIR}/thermostat-agent-@project.version@.jar:${THERM_DIR}/thermostat-common-@project.version@.jar:${THERM_DIR}/thermostat-tools-@project.version@.jar"
+SERVICE_MAIN="com.redhat.thermostat.tools.ThermostatService"
+
+${JAVA} -cp ${SERVICE_CLASSPATH} ${SERVICE_MAIN} "$@"
--- a/pom.xml	Thu Mar 29 23:52:18 2012 +0200
+++ b/pom.xml	Thu Mar 29 23:54:17 2012 +0200
@@ -62,6 +62,7 @@
     <module>agent</module>
     <module>client</module>
     <module>distribution</module>
+    <module>tools</module>
   </modules>
 
   <build>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/pom.xml	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,80 @@
+<?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.2-SNAPSHOT</version>
+    <relativePath>..</relativePath>
+  </parent>
+
+  <artifactId>thermostat-tools</artifactId>
+  <packaging>jar</packaging>
+
+  <name>Thermostat Tools</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>1.9.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/ThermostatService.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,138 @@
+/*
+ * 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.tools;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.redhat.thermostat.agent.AgentApplication;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+import com.redhat.thermostat.tools.db.DBService;
+
+/**
+ * Simple service that allows starting Agent and DB Backend
+ * in a single step.
+ */
+public class ThermostatService implements Application, ActionListener<ApplicationState> {
+    
+    private Application database;
+    private AgentApplication agent;
+    
+    private ActionNotifier<ApplicationState> notifier;
+    
+    public ThermostatService() {
+        database = new DBService();
+        agent = new AgentApplication();
+        notifier = new ActionNotifier<>(this);
+    }
+    
+    @Override
+    public void parseArguments(List<String> args) throws InvalidConfigurationException {
+        // Currently, all the arguments are for the db. We only configure the
+        // agent accordingly to the database settings.
+        // so nothing else is done here at this stage
+        database.parseArguments(args);
+        database.getNotifier().addActionListener(this);
+        agent.getNotifier().addActionListener(this);
+    }
+
+    @Override
+    public void run() {
+        // just run the database, if the database is successful, let the
+        // listeners start the agent for us.
+        database.run();
+    }
+
+    @Override
+    public void printHelp() {
+        // TODO, no, really, seriously
+    }
+
+    @Override
+    public ActionNotifier<ApplicationState> getNotifier() {
+        return notifier;
+    }
+
+    @Override
+    public StartupConfiguration getConfiguration() {
+        throw new NotImplementedException("NYI");
+    }
+    
+    public static void main(String[] args) throws IOException, InvalidConfigurationException {
+        ThermostatService service = new ThermostatService();
+        // TODO: other than failing, this should really print the help
+        // from the appropriate application instead, see the printHelp comment
+        // too.
+        service.parseArguments(Arrays.asList(args));
+        service.run();
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
+        if (actionEvent.getSource().equals(database)) {
+            switch (actionEvent.getActionId()) {
+            case SUCCESS:
+                String dbUrl = database.getConfiguration().getDBConnectionString();
+                List<String> args = new ArrayList<>();
+                args.add("--dbUrl");
+                args.add(dbUrl);
+                try {
+                    agent.parseArguments(args);
+                    System.err.println("starting agent now...");
+                    agent.run();
+                } catch (InvalidConfigurationException e) {
+                    notifier.fireAction(ApplicationState.FAIL);
+                }
+                break;
+            }
+        } else if (actionEvent.getSource().equals(agent)) {
+            // agent is running
+            switch (actionEvent.getActionId()) {
+            case START:
+                notifier.fireAction(ApplicationState.SUCCESS);
+                break;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/db/DBConfig.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,48 @@
+/*
+ * 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.tools.db;
+
+/**
+ * Set of configuration option that the {@link DBService} understands.
+ */
+enum DBConfig {
+
+    BIND,
+    LOCAL,
+    CLUSTER,
+    URL,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/db/DBOptionParser.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,160 @@
+/*
+ * 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.tools.db;
+
+import java.io.IOException;
+import java.util.List;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.config.ThermostatOptionParser;
+import com.redhat.thermostat.tools.ApplicationState;
+
+class DBOptionParser implements ThermostatOptionParser {
+    
+    private boolean quiet;
+    
+    private DBStartupConfiguration configuration;
+    
+    private List<String> args;
+    private OptionParser parser;
+
+    private DBArgs serviceAction;
+    
+    private boolean dryRun;
+    
+    DBOptionParser(DBStartupConfiguration configuration, List<String> args) {
+        this.args = args;
+        this.configuration = configuration;
+        parser = new OptionParser();
+    }
+    
+    @Override
+    public void parse() throws InvalidConfigurationException {
+        
+        parser.accepts(DBArgs.START.option, DBArgs.START.description);
+        parser.accepts(DBArgs.STOP.option, DBArgs.STOP.description);
+        
+        parser.accepts(DBArgs.DRY.option, DBArgs.DRY.description);
+        
+        parser.accepts(DBArgs.HELP.option, DBArgs.HELP.description);
+        parser.accepts(DBArgs.QUIET.option, DBArgs.QUIET.description);
+             
+        OptionSet options = parser.parse(args.toArray(new String[0]));
+        if (!options.hasOptions() || options.has(DBArgs.HELP.option)) {
+            displayHelp();
+            return;
+        }
+
+        if (options.has(DBArgs.START.option)) {
+            serviceAction = DBArgs.START;
+        } else if (options.has(DBArgs.STOP.option)) {
+            serviceAction = DBArgs.STOP;
+            
+        } else {
+            throw new InvalidConfigurationException("either --start or --stop must be given");
+        }
+
+        if (options.has(DBArgs.DRY.option)) {
+            dryRun = true;
+        }
+        
+        if (options.has(DBArgs.QUIET.option)) {
+            quiet = true;
+        }
+        
+        // leave at the end, since it depends on the previous settings
+        String url = configuration.getUrl();
+        long port = configuration.getLocalPort();
+        configuration.setLocal(true);
+        if (options.has(DBArgs.CLUSTER.option)) {
+            port = configuration.getClusterPort();
+            configuration.setLocal(false);
+        }
+        configuration.setDBConnectionString(url + ":" + port);
+    }
+
+    boolean isDryRun() {
+        return dryRun;
+    }
+    
+    @Override
+    public void displayHelp() {
+        
+        if (quiet) return;
+        
+        try {
+            System.out.println("Module [DBService]");
+            parser.printHelpOn(System.out);
+        } catch (IOException ignore) {}
+    }
+    
+    ApplicationState getAction() {
+        return serviceAction.state;
+    }
+    
+    static enum DBArgs {
+                        
+        CLUSTER("cluster", "launch the db in cluster mode, if not specified, " +
+                "local mode is the default", ApplicationState.NONE),
+                
+        DRY("dry-run", "run the service in dry run mode", ApplicationState.NONE),
+        
+        HELP("help", "print this usage help", ApplicationState.HELP),
+        
+        START("start", "start the database", ApplicationState.START),
+        STOP("stop", "stop the database", ApplicationState.STOP),
+        
+        QUIET("quiet", "don't produce any output", ApplicationState.NONE);
+        
+        private String option;
+        private String description;
+        private ApplicationState state;
+        
+        DBArgs(String option, String description, ApplicationState state) {
+            this.option = option;
+            this.description = description;
+            this.state = state;
+        }
+    }
+
+    boolean isQuiet() {
+        return quiet;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/db/DBService.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,277 @@
+/*
+ * 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.tools.db;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+import com.redhat.thermostat.common.config.ConfigUtils;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.utils.LoggedExternalProcess;
+import com.redhat.thermostat.tools.BasicApplication;
+
+public class DBService extends BasicApplication {
+
+    private DBStartupConfiguration configuration;
+    private DBOptionParser parser;
+    
+    private static final String [] MONGO_BASIC_ARGS = {
+        "mongod", "--quiet", "--fork", "--nojournal", "--noauth", "--bind_ip"
+    };
+    
+    private static final String [] MONGO_SHUTDOWN_ARGS = {
+        "mongod", "--shutdown", "--dbpath"
+    };
+    
+    @Override
+    public void parseArguments(List<String> 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();
+        
+    }
+    
+    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.LOCAL.name())) {
+            String port = (String) properties.get(DBConfig.LOCAL.name());
+            int localPort = Integer.parseInt(port);
+            configuration.setLocalPort(localPort);
+        } else {
+            throw new InvalidConfigurationException(DBConfig.LOCAL + " property missing");
+        }
+        
+        if (properties.containsKey(DBConfig.CLUSTER.name())) {
+            String port = (String) properties.get(DBConfig.CLUSTER.name());
+            int localPort = Integer.parseInt(port);
+            configuration.setClusterPort(localPort);
+        } else {
+            throw new InvalidConfigurationException(DBConfig.CLUSTER + " property missing");
+        }
+        
+        if (properties.containsKey(DBConfig.URL.name())) {
+            String url = (String) properties.get(DBConfig.URL.name());
+            configuration.setUrl(url);
+        } else {
+            throw new InvalidConfigurationException(DBConfig.URL + " 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 {
+        
+        String pid = checkPid();
+        if (pid != null) {
+            String message = "cannot start server " + configuration.getDBPath() +
+                    ", found pid file rom previous run, please, cleanup";
+            display(message);
+            notifyFail();
+            return;
+        }
+        
+        List<String> commands = new ArrayList<>(Arrays.asList(MONGO_BASIC_ARGS));
+       
+        // check that the db directory exist
+        display("starting storage server...");
+
+        commands.add(configuration.getBindIP());
+
+        commands.add("--dbpath");
+        commands.add(configuration.getDBPath().getCanonicalPath());
+
+        commands.add("--logpath");
+        commands.add(configuration.getLogFile().getCanonicalPath());
+
+        commands.add("--pidfilepath");
+        commands.add(configuration.getPidFile().getCanonicalPath());
+
+        commands.add("--port");
+        if (configuration.isLocal()) {
+            commands.add(Long.toString(configuration.getLocalPort()));
+        } else {
+            commands.add(Long.toString(configuration.getClusterPort()));
+        }
+        
+        LoggedExternalProcess process = new LoggedExternalProcess(commands);
+        int status = process.runAndReturnResult();
+        if (status == 0) {
+            pid = checkPid();
+            if (pid == null) status = -1;
+        }
+        
+        if (status == 0) {
+            display("server listening on ip: " + configuration.getDBConnectionString());
+            display("log file is here: " + configuration.getLogFile());
+            display("pid: " + pid);
+            notifySuccess();
+            
+        } else {
+            
+            String message = "cannot start server " + configuration.getDBPath() +
+                             ", exit status: " + status +
+                             ". Please check that your configuration is valid";
+            display(message);
+            notifyFail();
+        }
+    }
+    
+    private String checkPid() {
+        String pid = null;
+        // check the pid to be sure
+        File pidfile = configuration.getPidFile();
+        Charset charset = Charset.defaultCharset();
+        if (pidfile.exists()) {
+            try (BufferedReader reader = Files.newBufferedReader(pidfile.toPath(), charset)) {
+                pid = reader.readLine();
+                if (pid == null || pid.isEmpty()) {
+                    pid = null;
+                }
+            } catch (IOException ignore) {
+                ignore.printStackTrace();
+                pid = null;
+            }
+        }
+        
+        return pid;
+    }
+    
+    private void stopService() throws IOException, InterruptedException, InvalidConfigurationException {
+        check();
+        
+        List<String> commands = new ArrayList<>(Arrays.asList(MONGO_SHUTDOWN_ARGS));
+        commands.add(configuration.getDBPath().getCanonicalPath());
+
+        LoggedExternalProcess process = new LoggedExternalProcess(commands);
+        int status = process.runAndReturnResult();
+        if (status == 0) {
+            display("server shutdown complete: " + configuration.getDBPath());
+            display("log file is here: " + configuration.getLogFile());
+            notifySuccess();
+            
+        } else {
+            // TODO: check the pid and see if it's running or not
+            // perhaps was already down
+            String message = "cannot shutdown server " + configuration.getDBPath() +
+                    ", exit status: " + status +
+                    ". Please check that your configuration is valid";
+            display(message);
+            notifyFail();
+        }
+    }
+    
+    @Override
+    public void run() {
+        
+        if (parser.isDryRun()) return;
+        
+        try {
+            switch (parser.getAction()) {
+            case START:
+                startService();
+                break;
+            case STOP:
+                stopService();
+                break;
+            }
+        } catch (Exception e) {
+            // TODO: exception should be at least logged
+            notifyFail();
+        }
+    }
+    
+    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 void printHelp() {
+        parser.displayHelp();
+    }
+    
+    @Override
+    public DBStartupConfiguration getConfiguration() {
+        return configuration;
+    }
+    
+    private void display(String message) {
+        if (!parser.isQuiet()) {
+            System.out.println(message);
+        }
+    }
+    
+    public static void main(String[] args) throws InvalidConfigurationException {
+        DBService service = new DBService();
+        service.parseArguments(Arrays.asList(args));
+        service.run();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/db/DBStartupConfiguration.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,128 @@
+/*
+ * 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.tools.db;
+
+import java.io.File;
+
+import com.redhat.thermostat.common.config.ConfigUtils;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+
+class DBStartupConfiguration implements StartupConfiguration {
+    
+    private File dbPath;
+    private File logFile;
+    private File pidFile;
+        
+    private long localPort;
+    private long clusterPort;
+    
+    private String url;
+    
+    private String dbConnectionString;
+    
+    private String ip;
+    
+    private boolean local;
+    
+    public DBStartupConfiguration() throws InvalidConfigurationException {
+        dbPath = ConfigUtils.getStorageDirectory();
+        logFile = ConfigUtils.getStorageLogFile();
+        pidFile = ConfigUtils.getStoragePidFile();
+    }
+    
+    public File getDBPath() {
+        return dbPath;
+    }
+    
+    public File getLogFile() {
+        return logFile;
+    }
+    
+    public File getPidFile() {
+        return pidFile;
+    }
+   
+    void setLocalPort(long localPort) {
+        this.localPort = localPort;
+    }
+    
+    public long getLocalPort() {
+        return localPort;
+    }
+    
+    public long getClusterPort() {
+        return clusterPort;
+    }
+    
+    void setClusterPort(long clusterPort) {
+        this.clusterPort = clusterPort;
+    }
+    
+    void setUrl(String url) {
+        this.url = url;
+    }
+    
+    public String getUrl() {
+        return url;
+    }
+    
+    void setDBConnectionString(String dbConnectionString) {
+        this.dbConnectionString = dbConnectionString;
+    }
+    
+    @Override
+    public String getDBConnectionString() {
+        return dbConnectionString;
+    }
+
+    void setBindIP(String ip) {
+        this.ip = ip;
+    }
+    
+    public String getBindIP() {
+        return ip;
+    }
+    
+    void setLocal(boolean local) {
+        this.local = local;
+    }
+    
+    public boolean isLocal() {
+        return local;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/test/java/com/redhat/thermostat/tools/db/DBServiceTest.java	Thu Mar 29 23:54:17 2012 +0200
@@ -0,0 +1,202 @@
+/*
+ * 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.tools.db;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.tools.ApplicationState;
+
+public class DBServiceTest {
+    
+    private static final String LOCAL = "27518";
+    private static final String CLUSTER = "27517";
+    
+    private static final String BIND = "127.0.0.1";
+    
+    private static final String URL = "mongodb://127.0.0.1";
+    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.LOCAL.name(), LOCAL);
+            props.setProperty(DBConfig.CLUSTER.name(), CLUSTER);
+            props.setProperty(DBConfig.URL.name(), URL);
+
+            props.store(new FileOutputStream(tmpConfigs), "thermostat test properties");
+            
+        } catch (IOException e) {
+            Assert.fail("cannot setup tests: " + e);
+        }
+    }
+    
+    @Test
+    public void testConfig() throws InvalidConfigurationException {
+        
+        List<String> args = new ArrayList<>();
+        args.add("--quiet");
+        args.add("--start");
+        args.add("--dry-run");
+        
+        DBService service = new DBService();
+        service.parseArguments(args);
+
+        service.run();
+        
+        DBStartupConfiguration conf = service.getConfiguration();
+        
+        Assert.assertEquals(tmpDir + DB, conf.getDBPath().getPath());
+        Assert.assertEquals(Integer.parseInt(LOCAL), conf.getLocalPort());
+        Assert.assertEquals(Integer.parseInt(CLUSTER), conf.getClusterPort());
+        Assert.assertEquals(URL, conf.getUrl());
+    }
+    
+    @Test
+    public void testListeners() throws InvalidConfigurationException, InterruptedException {
+        List<String> args = new ArrayList<>();
+        args.add("--quiet");
+        args.add("--start");
+        
+        DBService service = new DBService();
+        service.parseArguments(args);
+        
+        final CountDownLatch latch = new CountDownLatch(0);
+        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] = false;
+                    break;
+                case SUCCESS:
+                    result[0] = true;
+                    break;
+                }
+                latch.countDown();
+            }
+        });
+        
+        service.run();
+        latch.await();
+        
+        Assert.assertTrue(result[0]);
+    
+        // FIXME: the server doesn't stop right away, but it's detached
+        // so we need to give some time... but there should be a better
+        // way than sleep...
+        Thread.sleep(2500);
+        
+        args = new ArrayList<>();
+        args.add("--quiet");
+        args.add("--stop");
+
+        // stop everything
+        service.parseArguments(args);
+        service.run();
+    }
+    
+    @Test
+    public void testListenersFail() throws InvalidConfigurationException, InterruptedException {
+        List<String> args = new ArrayList<>();
+        args.add("--quiet");
+        
+        // unlikely is already running, since it's a random db directory...
+        args.add("--stop");
+
+        DBService service = new DBService();
+        service.parseArguments(args);
+        
+        final CountDownLatch latch = new CountDownLatch(0);
+        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();
+        latch.await();
+        
+        Assert.assertTrue(result[0]);
+    }
+}