changeset 3:0d1762f6bcd9

Agent gets configuration at startup from properties file and command line.
author Jon VanAlten <jon.vanalten@redhat.com>
date Tue, 22 Nov 2011 13:36:10 -0500
parents 13a5cb11acd7
children de86f05b072b
files .hgignore config/agent.properties src/com/redhat/thermostat/agent/Agent.java src/com/redhat/thermostat/agent/AgentState.java src/com/redhat/thermostat/agent/AgentStateListener.java src/com/redhat/thermostat/agent/ArgumentParser.java src/com/redhat/thermostat/agent/Defaults.java src/com/redhat/thermostat/agent/Main.java src/com/redhat/thermostat/agent/RuntimeConfiguration.java src/com/redhat/thermostat/agent/StartupConfiguration.java src/com/redhat/thermostat/agent/config/Configuration.java src/com/redhat/thermostat/backend/Backend.java src/com/redhat/thermostat/backend/BackendFeature.java src/com/redhat/thermostat/backend/BackendRegistry.java src/com/redhat/thermostat/common/Constants.java
diffstat 15 files changed, 315 insertions(+), 448 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Tue Nov 22 13:36:10 2011 -0500
@@ -0,0 +1,5 @@
+.settings/org.eclipse.jdt.ui.prefs
+.project
+.classpath
+bin
+
--- a/config/agent.properties	Mon Nov 21 18:22:13 2011 -0500
+++ b/config/agent.properties	Tue Nov 22 13:36:10 2011 -0500
@@ -5,7 +5,7 @@
 mongos_port=27517
 ### Below settings are used only by the mongo launch script
 config_port=27519
-config_uri=localhost
+config_url=mongodb://127.0.0.1
 #
 ## Backend Configuration
 # This must be a comma separated list naming each backend that should run
--- a/src/com/redhat/thermostat/agent/Agent.java	Mon Nov 21 18:22:13 2011 -0500
+++ b/src/com/redhat/thermostat/agent/Agent.java	Tue Nov 22 13:36:10 2011 -0500
@@ -1,17 +1,16 @@
 package com.redhat.thermostat.agent;
 
 import java.util.Arrays;
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.UUID;
-import java.util.concurrent.CopyOnWriteArrayList;
 
 import com.mongodb.BasicDBObject;
 import com.mongodb.DB;
 import com.mongodb.DBCollection;
 import com.mongodb.DBObject;
 import com.mongodb.WriteConcern;
+import com.redhat.thermostat.agent.config.Configuration;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
 import com.redhat.thermostat.common.Constants;
@@ -23,100 +22,45 @@
 
     private final UUID id;
     private final BackendRegistry backendRegistry;
-    private final List<AgentStateListener> listeners;
-    private final RuntimeConfiguration runtimeConfig;
-    private final StartupConfiguration startupConfig;
+    private final Configuration config;
 
-    private AgentState currentState;
     private DB database;
 
-    public Agent(BackendRegistry backendRegistry, StartupConfiguration startupConfiguration) {
-        this(backendRegistry,
-                UUID.randomUUID(),
-                startupConfiguration,
-                new RuntimeConfiguration());
+    public Agent(BackendRegistry backendRegistry, Configuration config, DB db) {
+        this(backendRegistry, UUID.randomUUID(), config, db);
     }
 
-    public Agent(BackendRegistry registry, UUID agentId, StartupConfiguration startup, RuntimeConfiguration runtime) {
+    public Agent(BackendRegistry registry, UUID agentId, Configuration config, DB db) {
         this.id = agentId;
         this.backendRegistry = registry;
-        this.listeners = new CopyOnWriteArrayList<AgentStateListener>();
-        this.currentState = AgentState.DISCONNECTED;
-        this.startupConfig = startup;
-        this.runtimeConfig = runtime;
-        updateConfig();
-    }
-
-    /**
-     * Update the agent configuration from backends
-     */
-    private void updateConfig() {
-        for (Backend backend : backendRegistry.getAll()) {
-            String isActive = Boolean.toString(Arrays.asList(startupConfig.getBackendsToStart()).contains(backend.getName()));
-            runtimeConfig.addConfig(backend.getName(), Constants.AGENT_CONFIG_KEY_BACKEND_ACTIVE, isActive);
-            Map<String, String> settings = backend.getSettings();
-            for (Entry<String, String> e : settings.entrySet()) {
-                runtimeConfig.addConfig(backend.getName(), e.getKey(), e.getValue());
-            }
-        }
-    }
-
-    public void setDatabase(DB database) {
-        this.database = database;
+        this.config = config;
+        this.database = db;
+        config.setAgent(this);
+        config.setCollection(database.getCollection(Constants.AGENT_CONFIG_COLLECTION_NAME));
+        loadConfiguredBackends();
     }
 
-    /**
-     * Advertises the agent as active to the world.
-     */
-    public void publish() {
-        DBCollection configCollection = database.getCollection(Constants.AGENT_CONFIG_COLLECTION_NAME);
-        DBObject toInsert = runtimeConfig.toBson();
-        toInsert.put(Constants.AGENT_ID, id.toString());
-        configCollection.insert(toInsert, WriteConcern.SAFE);
-        setState(AgentState.ACTIVE);
+    private void loadConfiguredBackends() {
+        // TODO Once Configuration has relevant methods for getting list of backend names and backend-specific parameters, iterate over that list,
+        // activating as per configuration parameters and adding each to the registry
     }
 
-    /**
-     * Removes the agent info published to the world
-     */
-    public void unpublish() {
-        DBCollection configCollection = database.getCollection(Constants.AGENT_CONFIG_COLLECTION_NAME);
-        BasicDBObject toRemove = new BasicDBObject(Constants.AGENT_ID, id.toString());
-        configCollection.remove(toRemove);
-        setState(AgentState.DISCONNECTED);
+    private void stopAllBackends() {
+        // TODO Inverse of the above.  Stop each backend, remove from registry.
     }
 
-    public synchronized AgentState getState() {
-        return currentState;
-    }
-
-    public synchronized void setState(AgentState newState) {
-        if (currentState != newState) {
-            currentState = newState;
-            emitStateChanged();
-        }
+    public void start() {
+        loadConfiguredBackends();
+        config.publish();
     }
 
-    private void emitStateChanged() {
-        for (AgentStateListener listener : listeners) {
-            listener.stateChanged(this);
-        }
-    }
-
-    public void addStateListener(AgentStateListener listener) {
-        listeners.add(listener);
-    }
-
-    public void removeStateListener(AgentStateListener listener) {
-        listeners.remove(listener);
+    public void stop() {
+        config.unpublish();
+        stopAllBackends();
     }
 
     public UUID getId() {
         return id;
     }
 
-    public BackendRegistry getBackendRegistry() {
-        return backendRegistry;
-    }
-
 }
--- a/src/com/redhat/thermostat/agent/AgentState.java	Mon Nov 21 18:22:13 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-package com.redhat.thermostat.agent;
-
-public enum AgentState {
-    /**
-     * the agent in the default state. It has no connection. It is not doing
-     * anything
-     */
-    DISCONNECTED,
-    /** just connected. setting up data structures */
-    CONNECTED,
-    /** collecting data and doing stuff */
-    ACTIVE,
-    /**
-     * server was disconnected abruptly. cache data until we can send it over
-     * again
-     */
-    CACHING,
-
-}
--- a/src/com/redhat/thermostat/agent/AgentStateListener.java	Mon Nov 21 18:22:13 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-package com.redhat.thermostat.agent;
-
-public interface AgentStateListener {
-    public void stateChanged(Agent agent);
-}
--- a/src/com/redhat/thermostat/agent/ArgumentParser.java	Mon Nov 21 18:22:13 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-package com.redhat.thermostat.agent;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.logging.Level;
-
-import com.redhat.thermostat.common.Constants;
-
-public class ArgumentParser {
-
-    private final boolean inSingleMode;
-    private final String connectionURL;
-    private final Level logLevel;
-
-    public ArgumentParser(StartupConfiguration startupConfig, String[] args) {
-        List<String> arguments = new ArrayList<String>(Arrays.asList(args));
-        boolean single = false;
-        String url = Constants.MONGO_DEFAULT_URL;
-        Level level = Level.WARNING;
-        int port = -1;
-
-        int index = 0;
-        while (index < arguments.size()) {
-            if (arguments.get(index).equals("--loglevel")) {
-                int next = index + 1;
-                if (next < arguments.size()) {
-                    level = Level.parse(arguments.get(next).toUpperCase());
-                    System.out.println("log level is: " + level);
-                    index++;
-                }
-            } else if (arguments.get(index).equals("--local")) {
-                single = true;
-            } else if (arguments.get(index).equals("--remote")) {
-                single = false;
-                int next = index + 1;
-                if (next < arguments.size()) {
-                    port = Integer.parseInt(arguments.get(next));
-                    index++;
-                }
-            }
-
-            index++;
-        }
-
-        if (port != -1) {
-            url = url + ":" + port + "/";
-        } else if (single) {
-            url = url + ":" + startupConfig.getPortForLocal() + "/";
-        } else {
-            url = url + ":" + startupConfig.getPortForRemote() + "/";
-        }
-
-        inSingleMode = single;
-        connectionURL = url;
-        logLevel = level;
-    }
-
-    public boolean inSingleMode() {
-        return inSingleMode;
-    }
-
-    public String getConnectionURL() {
-        return connectionURL;
-    }
-
-    public Level getLogLevel() {
-        return logLevel;
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/agent/Defaults.java	Tue Nov 22 13:36:10 2011 -0500
@@ -0,0 +1,14 @@
+package com.redhat.thermostat.agent;
+
+import java.util.logging.Level;
+
+public final class Defaults {
+
+	/* Should not be instantiated */
+	private Defaults() {
+	}
+
+    public static final Level LOGGING_LEVEL = Level.WARNING;
+    public static final boolean local = false; // Default behaviour is to connect to cluster.
+
+}
--- a/src/com/redhat/thermostat/agent/Main.java	Mon Nov 21 18:22:13 2011 -0500
+++ b/src/com/redhat/thermostat/agent/Main.java	Tue Nov 22 13:36:10 2011 -0500
@@ -4,12 +4,14 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.net.UnknownHostException;
+import java.util.Properties;
 import java.util.logging.LogManager;
 import java.util.logging.Logger;
 
 import com.mongodb.DB;
 import com.mongodb.Mongo;
 import com.mongodb.MongoURI;
+import com.redhat.thermostat.agent.config.Configuration;
 import com.redhat.thermostat.backend.BackendRegistry;
 import com.redhat.thermostat.common.Constants;
 import com.redhat.thermostat.common.utils.LoggingUtils;
@@ -32,45 +34,49 @@
         LoggingUtils.resetAndGetRootLogger();
         Logger logger = LoggingUtils.getLogger(Main.class);
 
-        StartupConfiguration startupConfig = null;
+        Properties props = null;
         try {
-            startupConfig = new StartupConfiguration(new FileReader(Constants.AGENT_CONFIG_FILE_LOCATION));
+            props = new Properties();
+            props.load(new FileReader(Constants.AGENT_PROPERTIES_FILE));
         } catch (FileNotFoundException fnfe) {
-            System.err.println("unable to read config file at " + Constants.AGENT_CONFIG_FILE_LOCATION);
+            System.err.println("Unable to read properties file at " + Constants.AGENT_PROPERTIES_FILE);
+            System.exit(Constants.EXIT_UNABLE_TO_READ_CONFIG);
+        } catch (IOException e) {
+            System.err.println("Unable to read properties file at " + Constants.AGENT_PROPERTIES_FILE);
             System.exit(Constants.EXIT_UNABLE_TO_READ_CONFIG);
         }
 
-        ArgumentParser argumentParser = new ArgumentParser(startupConfig, args);
+        Configuration config = new Configuration(args, props);
 
-        logger.setLevel(argumentParser.getLogLevel());
+        logger.setLevel(config.getLogLevel());
 
         BackendRegistry backendRegistry = BackendRegistry.getInstance();
 
         Mongo mongo = null;
+        DB db = null;
         try {
-            String uri = argumentParser.getConnectionURL();
-            logger.fine("connecting to " + uri);
-            mongo = new Mongo(new MongoURI(uri));
-            DB db = mongo.getDB(Constants.THERMOSTAT_DB);
+            MongoURI mongoURI = config.getMongoURI();
+            mongo = new Mongo(mongoURI);
+            db = mongo.getDB(Constants.THERMOSTAT_DB);
             logger.fine("connected");
-            Agent agent = new Agent(backendRegistry, startupConfig);
-            agent.setDatabase(db);
-            agent.publish();
-            logger.fine("agent published");
-
-            try {
-                System.in.read();
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-
-            agent.unpublish();
-            logger.fine("agent unpublished");
         } catch (UnknownHostException uhe) {
             System.err.println("unknown host");
             uhe.printStackTrace();
             System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
         }
 
+        Agent agent = new Agent(backendRegistry, config, db);
+        agent.start();
+        logger.fine("agent published");
+
+        try {
+            System.in.read();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        agent.stop();
+        logger.fine("agent unpublished");
+        
     }
 }
--- a/src/com/redhat/thermostat/agent/RuntimeConfiguration.java	Mon Nov 21 18:22:13 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-package com.redhat.thermostat.agent;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Random;
-import java.util.Set;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.common.Constants;
-
-/**
- * The current run time configuration of the agent.
- */
-public class RuntimeConfiguration {
-
-    private String agentName;
-    private Backends backendConfig;
-
-    public RuntimeConfiguration() {
-        agentName = "agent " + new Random().nextLong();
-        backendConfig = new Backends();
-    }
-
-    public static RuntimeConfiguration fromBson(DBObject remote) {
-        RuntimeConfiguration config = new RuntimeConfiguration();
-        BasicDBObject other = (BasicDBObject) remote;
-        // TODO deal with missing/bad keys/values
-        config.agentName = other.getString(Constants.AGENT_CONFIG_KEY_AGENT_NAME);
-        BasicDBObject remoteBackends = (BasicDBObject) other.get(Constants.AGENT_CONFIG_KEY_BACKENDS);
-        for (Entry<String, Object> remoteBackendEntry : remoteBackends.entrySet()) {
-            String backendName = remoteBackendEntry.getKey();
-            BasicDBObject backendConfig = (BasicDBObject) remoteBackendEntry.getValue();
-            for (Entry<String, Object> e : backendConfig.entrySet()) {
-                config.backendConfig.addConfig(backendName, e.getKey(), (String) e.getValue());
-            }
-        }
-        return config;
-    }
-
-    public DBObject toBson() {
-        BasicDBObject result = new BasicDBObject();
-        result.put(Constants.AGENT_CONFIG_KEY_AGENT_NAME, agentName);
-        result.put(Constants.AGENT_CONFIG_KEY_BACKENDS, backendConfig.toDBObject());
-        return result;
-    }
-
-    public void setAvailableBackends(String[] backends) {
-        for (String backend : backends) {
-            backendConfig.addBackend(backend);
-        }
-    }
-
-    public String[] getAllBackends() {
-        return backendConfig.getBackends().toArray(new String[0]);
-    }
-
-    public String[] getActiveBackends() {
-        return backendConfig.getMatchingBackends(Constants.AGENT_CONFIG_KEY_BACKEND_ACTIVE, Boolean.TRUE.toString()).toArray(new String[0]);
-    }
-
-    public void addConfig(String backend, String key, String value) {
-        backendConfig.addConfig(backend, key, value);
-    }
-
-    public String getConfig(String backendName, String configKey) {
-        return backendConfig.getConfig(backendName).get(configKey);
-    }
-
-    /**
-     * A wrapper around the backend-specific information
-     */
-    private static class Backends {
-        /** {backend-name: { opt1: va1, opt2:val2, } } */
-        private Map<String, Map<String, String>> info;
-
-        public Backends() {
-            info = new HashMap<String, Map<String, String>>();
-        }
-
-        public void addBackend(String backendName) {
-            if (!info.containsKey(backendName)) {
-                info.put(backendName, new HashMap<String, String>());
-            }
-        }
-
-        public Object toDBObject() {
-            BasicDBObject result = new BasicDBObject();
-            for (Entry<String, Map<String, String>> e : info.entrySet()) {
-                BasicDBObject config = new BasicDBObject();
-                config.putAll(e.getValue());
-                result.put(e.getKey(), config);
-            }
-            return result;
-        }
-
-        public Set<String> getBackends() {
-            return info.keySet();
-        }
-
-        public Set<String> getMatchingBackends(String key, String value) {
-            // TODO perhaps extend this to regex?
-            Set<String> matched = new HashSet<String>();
-            for (Entry<String, Map<String, String>> e : info.entrySet()) {
-                if (e.getValue().get(key) != null && e.getValue().get(key).equals(value)) {
-                    matched.add(e.getKey());
-                }
-            }
-            return matched;
-        }
-
-        public void addConfig(String backend, String key, String value) {
-            addBackend(backend);
-            info.get(backend).put(key, value);
-        }
-
-        public Map<String, String> getConfig(String backend) {
-            return info.get(backend);
-        }
-
-    }
-
-}
--- a/src/com/redhat/thermostat/agent/StartupConfiguration.java	Mon Nov 21 18:22:13 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-package com.redhat.thermostat.agent;
-
-import java.io.Reader;
-import java.util.Properties;
-
-/**
- * Configuration used by the agent during startup, initialization. As opposed to
- * {@link RuntimeConfiguration}, this configuration is not exported.
- */
-public class StartupConfiguration {
-
-    private final int localPort;
-    private final int remotePort;
-    private final String[] backends;
-
-    public StartupConfiguration(Reader configReader) {
-        Properties properties = new Properties();
-        try {
-            properties.load(configReader);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        localPort = Integer.valueOf(properties.getProperty("mongod_port"));
-        remotePort = Integer.valueOf(properties.getProperty("mongos_port"));
-        backends = properties.getProperty("backends").trim().split(",");
-
-    }
-
-    public int getPortForLocal() {
-        return localPort;
-    }
-
-    public int getPortForRemote() {
-        return remotePort;
-    }
-
-    public String[] getBackendsToStart() {
-        return backends;
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/agent/config/Configuration.java	Tue Nov 22 13:36:10 2011 -0500
@@ -0,0 +1,211 @@
+package com.redhat.thermostat.agent.config;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.logging.Level;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.mongodb.MongoURI;
+import com.mongodb.WriteConcern;
+import com.redhat.thermostat.agent.Agent;
+import com.redhat.thermostat.agent.Defaults;
+import com.redhat.thermostat.common.Constants;
+
+public final class Configuration {
+    private Arguments arguments;
+    private MongoURI uri;
+    private Backends backends;
+    private String hostname;
+    private Agent agent;
+    private DBCollection dbCollection = null;
+    private boolean published = false;
+
+    public Configuration(String[] args, Properties props) {
+        arguments = new Arguments(args);
+        if (arguments.getLocalMode()) {
+            uri = new MongoURI(Constants.MONGO_URL + ":" + props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT));
+            hostname = Constants.AGENT_LOCAL_HOSTNAME;
+        } else {
+            uri = new MongoURI(props.getProperty(Constants.MONGO_URL) + ":" + props.getProperty(Constants.AGENT_PROPERTY_MONGOS_PORT));
+            try {
+                InetAddress addr = InetAddress.getLocalHost();
+                hostname = addr.getCanonicalHostName();
+            } catch (UnknownHostException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }
+        backends = new Backends(props);
+    }
+
+
+    public void setCollection(DBCollection collection) {
+        dbCollection = collection;
+    }
+    
+    public Level getLogLevel() {
+        return arguments.getLogLevel();
+    }
+
+    public MongoURI getMongoURI() {
+        return uri;
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public DBObject toDBObject() {
+        BasicDBObject result = new BasicDBObject();
+        // TODO explicit exception if agent not yet set.
+        result.put(Constants.AGENT_ID, agent.getId().toString());
+        result.put(Constants.AGENT_CONFIG_KEY_HOST, hostname);
+        result.put(Constants.AGENT_CONFIG_KEY_BACKENDS, backends.toDBObject());
+        return result;
+    }
+
+    public void setAgent(Agent agent) {
+        this.agent = agent;
+    }
+
+    public void publish() {
+        // TODO explicit exception if dbCollection has not yet been set.
+        dbCollection.insert(toDBObject(), WriteConcern.SAFE);
+        // TODO Start configuration-change-detection thread.
+        published = true;
+    }
+
+    public void unpublish() {
+        // TODO Stop configuration-change-detection thread.
+        dbCollection.remove(new BasicDBObject(Constants.AGENT_ID, agent.getId().toString()), WriteConcern.NORMAL);
+        published = false;
+    }
+
+    public String getBackendConfigValue(String backendName, String configurationKey) {
+        String value = null;
+        if (published) {
+            value = getBackendConfigFromDatabase(backendName, configurationKey);
+        } else {
+            value = backends.getConfigValue(backendName, configurationKey);
+        }
+        return value;
+    }
+
+    private String getBackendConfigFromDatabase(String backendName, String configurationKey) {
+        DBObject config = dbCollection.findOne(new BasicDBObject(Constants.AGENT_ID, agent.getId().toString()));
+        // TODO get the appropriate value from this agent's configuration.
+        return null;
+    }
+
+    private class Arguments {
+        private final boolean localMode;
+        private final Level logLevel;
+
+        public Arguments(String[] args) {
+            boolean local = Defaults.local;
+            Level level = Defaults.LOGGING_LEVEL;
+            for (int index = 0; index < args.length; index++) {
+                if (args[index].equals(Constants.AGENT_ARGUMENT_LOGLEVEL)) {
+                    index++;
+                    if (index < args.length) {
+                        level = Level.parse(args[index].toUpperCase());
+                    } else {
+                        
+                    }
+                } else if (args[index].equals(Constants.AGENT_ARGUMENT_LOCAL)) {
+                    local = true;
+                }
+            }
+            logLevel = level;
+            localMode = local;
+        }
+
+        public boolean getLocalMode() {
+            return localMode;
+        }
+
+        public Level getLogLevel() {
+            return logLevel;
+        }
+    }
+
+    /**
+     * A wrapper around the backend-specific information.  Used mainly for convenience during startup; once running all config should happen via database.
+     */
+    private static class Backends {
+        /* TODO Do we really need to do all this mapping?  These values should only be used at startup, maybe best to just have convenience wrappers
+         * around the properties file...
+         */
+        /** {backend-name: { opt1: va1, opt2:val2, } } */
+        private Map<String, Map<String, String>> info;
+
+        public Backends(Properties props) {
+            info = new HashMap<String, Map<String, String>>();
+            initializeFromProperties(props);
+        }
+
+        private void initializeFromProperties(Properties props) {
+            List<String> backendNames = Arrays.asList(props.getProperty(Constants.AGENT_PROPERTY_BACKENDS).trim().split(","));
+            for (String backendName : backendNames) {
+                // TODO Initialize Map<String, String> of properties for each.
+            }
+        }
+
+        public String getConfigValue(String backendName, String configurationKey) {
+            // TODO make this more robust, appropriate exceptions for invalid input values.
+            Map<String, String> backendValues = info.get(backendName);
+            return backendValues.get(configurationKey);
+        }
+
+        private void addBackend(String backendName) {
+            if (!info.containsKey(backendName)) {
+                info.put(backendName, new HashMap<String, String>());
+            }
+        }
+
+        public Object toDBObject() {
+            BasicDBObject result = new BasicDBObject();
+            for (Entry<String, Map<String, String>> e : info.entrySet()) {
+                BasicDBObject config = new BasicDBObject();
+                config.putAll(e.getValue());
+                result.put(e.getKey(), config);
+            }
+            return result;
+        }
+
+        public Set<String> getBackends() {
+            return info.keySet();
+        }
+
+        public Set<String> getMatchingBackends(String key, String value) {
+            // TODO perhaps extend this to regex?
+            Set<String> matched = new HashSet<String>();
+            for (Entry<String, Map<String, String>> e : info.entrySet()) {
+                if (e.getValue().get(key) != null && e.getValue().get(key).equals(value)) {
+                    matched.add(e.getKey());
+                }
+            }
+            return matched;
+        }
+
+        public void addConfig(String backend, String key, String value) {
+            addBackend(backend);
+            info.get(backend).put(key, value);
+        }
+
+        public Map<String, String> getConfig(String backend) {
+            return info.get(backend);
+        }
+
+    }
+}
--- a/src/com/redhat/thermostat/backend/Backend.java	Mon Nov 21 18:22:13 2011 -0500
+++ b/src/com/redhat/thermostat/backend/Backend.java	Tue Nov 22 13:36:10 2011 -0500
@@ -1,35 +1,42 @@
 package com.redhat.thermostat.backend;
 
 import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * Represents a monitoring back-end. All the {@link Backend}s should be
  * registered with the {@link BackendRegistry}.
  */
-public interface Backend {
+public abstract class Backend {
+    private String name;
+
+    public Backend(String name, Map<String, String> properties) {
+        this.name = name;
+        for (Entry<String, String> e : properties.entrySet()) {
+            setConfigurationValue(e.getKey(), e.getValue());
+        }
+    }
+
+    public abstract void setConfigurationValue(String name, String value);
 
     /** Returns the name of the {@link Backend} */
-    public String getName();
+    public String getName() {
+        return name;
+    }
 
     /** Returns the description of the {@link Backend} */
-    public String getDescription();
+    public abstract String getDescription();
 
     /** Returns the vendor of the {@link Backend} */
-    public String getVendor();
+    public abstract String getVendor();
 
     /** Returns the version of the {@link Backend} */
-    public String getVersion();
-
-    /**
-     * Returns an array of {@link BackendFeature}s supported by this backend. To
-     * indicate no supported features, return an empty array, not null.
-     */
-    public BackendFeature[] getSupportedFeatures();
+    public abstract String getVersion();
 
     /**
      * Returns a map containing the settings of this backend
      */
-    public Map<String, String> getSettings();
+    public abstract Map<String, String> getConfigurationMap();
 
     /**
      * Activate the {@link Backend}. The backend should start gathering general
@@ -37,7 +44,7 @@
      *
      * @return true on success, false if there was an error
      */
-    public boolean activate();
+    public abstract boolean activate();
 
     /**
      * Deactivate the {@link Backend}. The backend should release resources at
@@ -45,12 +52,12 @@
      *
      * @return true on success
      */
-    public boolean deactivate();
+    public abstract boolean deactivate();
 
     /**
      * Returns a boolean indicating if the backend is currently active on this
      * host
      */
-    public boolean isActive();
+    public abstract boolean isActive();
 
 }
--- a/src/com/redhat/thermostat/backend/BackendFeature.java	Mon Nov 21 18:22:13 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-package com.redhat.thermostat.backend;
-
-/**
- * A specific feature provided by a {@link Backend}. Each {@link Backend}
- * provides a different set of features. Some may even provide none.
- */
-public class BackendFeature {
-    public static final BackendFeature MXBEANS = new BackendFeature("mxbeans");
-    public static final BackendFeature PERF_COUNTER = new BackendFeature("perfcounter");
-    public static final BackendFeature PROC = new BackendFeature("proc");
-    public static final BackendFeature STARTUP_ARGUMENTS = new BackendFeature("startup-arguments");
-    public static final BackendFeature SYSTEM_PROPERTIES = new BackendFeature("system-properties");
-    public static final BackendFeature HEAP = new BackendFeature("heap");
-
-    private String featureName;
-
-    public BackendFeature(String featureName) {
-        this.featureName = featureName;
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (other == null) {
-            return false;
-        }
-        if (other == this) {
-            return true;
-        }
-        if (other.getClass() == this.getClass()) {
-            BackendFeature otherFeature = (BackendFeature) other;
-            if (otherFeature.featureName.equals(featureName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return featureName.hashCode();
-    }
-
-}
--- a/src/com/redhat/thermostat/backend/BackendRegistry.java	Mon Nov 21 18:22:13 2011 -0500
+++ b/src/com/redhat/thermostat/backend/BackendRegistry.java	Tue Nov 22 13:36:10 2011 -0500
@@ -1,33 +1,20 @@
 package com.redhat.thermostat.backend;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.ServiceLoader;
-import java.util.Set;
 
 /**
  * A registry for {@link Backend}s. Each {@link Backend} should call
- * {@link #register(Backend, BackendFeature[])} to register itself.
+ * {@link #register(Backend)} to register itself.
  */
 public final class BackendRegistry {
 
     private static BackendRegistry INSTANCE = null;
 
-    private static ServiceLoader<Backend> backendLoader = ServiceLoader.load(Backend.class);
-
     private final List<Backend> registeredBackends;
-    private final Map<BackendFeature, Set<Backend>> featureToBackend;
 
     private BackendRegistry() {
         registeredBackends = new ArrayList<Backend>();
-        featureToBackend = new HashMap<BackendFeature, Set<Backend>>();
-
-        for (Backend newBackend : backendLoader) {
-            register(newBackend, newBackend.getSupportedFeatures());
-        }
     }
 
     public static synchronized BackendRegistry getInstance() {
@@ -37,14 +24,8 @@
         return INSTANCE;
     }
 
-    public synchronized void register(Backend backend, BackendFeature[] features) {
+    public synchronized void register(Backend backend) {
         registeredBackends.add(backend);
-        for (BackendFeature feature : features) {
-            if (!featureToBackend.containsKey(feature)) {
-                featureToBackend.put(feature, new HashSet<Backend>());
-            }
-            featureToBackend.get(feature).add(backend);
-        }
     }
 
     public synchronized void unregister(Backend backend) {
@@ -63,9 +44,4 @@
         }
         return null;
     }
-
-    public synchronized Backend[] getBackendsForFeature(BackendFeature feature) {
-        return featureToBackend.get(feature).toArray(new Backend[0]);
-    }
-
 }
--- a/src/com/redhat/thermostat/common/Constants.java	Mon Nov 21 18:22:13 2011 -0500
+++ b/src/com/redhat/thermostat/common/Constants.java	Tue Nov 22 13:36:10 2011 -0500
@@ -8,22 +8,30 @@
 
     public static final String LOGGING_CONFIG = "com.redhat.thermostat.level=ALL";
 
-    public static final String AGENT_CONFIG_FILE_LOCATION = "config/agent.properties";
+    public static final String AGENT_PROPERTIES_FILE = "config/agent.properties";
 
     public static final int EXIT_UNKNOWN_ERROR = 1;
     public static final int EXIT_UNABLE_TO_CONNECT_TO_DATABASE = 2;
     public static final int EXIT_UNABLE_TO_READ_CONFIG = 3;
 
     public static final String THERMOSTAT_DB = "thermostat";
-    public static final String MONGO_DEFAULT_URL = "mongodb://127.0.0.1";
+    public static final String MONGO_URL = "mongodb://127.0.0.1";
 
     public static final String AGENT_CONFIG_COLLECTION_NAME = "agent-configs";
     public static final String AGENT_ID = "agent-id";
 
     public static final int SAMPLING_INTERVAL_UNKNOWN = -1;
 
-    public static final String AGENT_CONFIG_KEY_AGENT_NAME = "agent-name";
+    public static final String AGENT_CONFIG_KEY_HOST = "host";
     public static final String AGENT_CONFIG_KEY_BACKENDS = "backends";
     public static final String AGENT_CONFIG_KEY_BACKEND_ACTIVE = "active";
 
+    public static final String AGENT_ARGUMENT_LOCAL = "--local";
+    public static final String AGENT_ARGUMENT_LOGLEVEL = "--loglevel";
+
+    public static final String AGENT_PROPERTY_MONGOS_PORT = "mongos_port";
+    public static final String AGENT_PROPERTY_MONGOD_PORT = "mongod_port";
+    public static final String AGENT_PROPERTY_BACKENDS = "backends";
+
+    public static final String AGENT_LOCAL_HOSTNAME = "localhost";
 }