Mercurial > hg > release > thermostat-0.11
changeset 117:0af8034d5ac9
Initial refactoring of Storage and Connection.
Reviewed-by: vanaltj
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-March/000327.html
line wrap: on
line diff
--- a/agent/src/main/java/com/redhat/thermostat/agent/Agent.java Mon Mar 19 12:39:41 2012 -0400 +++ b/agent/src/main/java/com/redhat/thermostat/agent/Agent.java Mon Mar 19 19:44:09 2012 +0100 @@ -40,10 +40,10 @@ import java.util.logging.Logger; import com.redhat.thermostat.agent.config.ConfigurationWatcher; -import com.redhat.thermostat.agent.config.StartupConfiguration; 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;
--- a/agent/src/main/java/com/redhat/thermostat/agent/Defaults.java Mon Mar 19 12:39:41 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +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.util.logging.Level; - -public final class Defaults { - - private Defaults() { - /* Should not be instantiated */ - } - - public static final Level LOGGING_LEVEL = Level.WARNING; - public static final String DATABASE_URI = "mongodb://127.0.0.1"; - public static final int MONGOS_PORT = 27517; - public static final int MONGOD_PORT = 27518; - public static final boolean LOCAL_MODE = false; // Default behaviour is to connect to cluster. - -}
--- a/agent/src/main/java/com/redhat/thermostat/agent/Main.java Mon Mar 19 12:39:41 2012 -0400 +++ b/agent/src/main/java/com/redhat/thermostat/agent/Main.java Mon Mar 19 19:44:09 2012 +0100 @@ -37,21 +37,24 @@ package com.redhat.thermostat.agent; import java.io.IOException; -import java.net.UnknownHostException; 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.agent.config.StartupConfiguration; 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.LoggedExternalProcess; import com.redhat.thermostat.common.utils.LoggingUtils; public final class Main { @@ -84,30 +87,20 @@ LoggingUtils.setGlobalLogLevel(config.getLogLevel()); - String mongoScript = config.getMongoLaunchScript(); + ConnectionProvider connProv = new MongoConnectionProvider(config); + Connection connection = connProv.createConnection(); if (config.getLocalMode()) { - try { - logger.fine("Starting private mongod instance."); - logger.finest("Mongo launch script at: " + mongoScript); - int result = new LoggedExternalProcess(new String[] { mongoScript, "start" }).runAndReturnResult(); - if (result != 0) { - logger.severe("Error starting local mongod instance."); - System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE); - } - } catch (IOException e) { - logger.log(Level.SEVERE, "Unable to execute script to start local mongod instance.", e); - System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE); - } catch (InterruptedException e) { - logger.log(Level.SEVERE, "Interrupted while starting local mongod instance.", e); - System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE); - } + connection.setType(ConnectionType.LOCAL); + } else { + connection.setType(ConnectionType.REMOTE); } - Storage storage = new MongoStorage(); + + Storage storage = new MongoStorage(connection); try { - storage.connect(config.getDatabaseURIAsString()); + storage.connect(); logger.fine("Storage configured with database URI."); - } catch (UnknownHostException uhe) { - logger.log(Level.SEVERE, "Could not initialize storage layer.", uhe); + } catch (ConnectionFailedException ex) { + logger.log(Level.SEVERE, "Could not initialize storage layer.", ex); System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE); } @@ -140,18 +133,5 @@ agent.stop(); logger.fine("Agent stopped."); - if (config.getLocalMode()) { - logger.fine("Stopping private mongod instance."); - try { - int result = new LoggedExternalProcess(new String[] { mongoScript, "stop" }).runAndReturnResult(); - if (result != 0) { - logger.severe("Error stopping local mongod instance."); - } - } catch (IOException e) { - logger.log(Level.WARNING, "Unable to execute script to stop private mongod instance.", e); - } catch (InterruptedException e) { - logger.log(Level.SEVERE, "Interrupted while stopping local mongod instance.", e); - } - } } }
--- a/agent/src/main/java/com/redhat/thermostat/agent/config/StartupConfiguration.java Mon Mar 19 12:39:41 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,269 +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.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.agent.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(); - - if (localMode) { - completeDatabaseURI = databaseURI + ":" + mongodPort; - hostname = Constants.AGENT_LOCAL_HOSTNAME; - } else { - completeDatabaseURI = databaseURI + ":" + mongosPort; - 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 String getDatabaseURIAsString() { - return completeDatabaseURI; - } - - public String getHostname() { - return hostname; - } - - 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; - } - } -}
--- a/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java Mon Mar 19 12:39:41 2012 -0400 +++ b/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java Mon Mar 19 19:44:09 2012 +0100 @@ -43,8 +43,8 @@ import java.util.logging.Level; import java.util.logging.Logger; -import com.redhat.thermostat.agent.config.StartupConfiguration; import com.redhat.thermostat.backend.system.SystemBackend; +import com.redhat.thermostat.common.config.StartupConfiguration; import com.redhat.thermostat.common.storage.Storage; import com.redhat.thermostat.common.utils.LoggingUtils;
--- a/agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java Mon Mar 19 12:39:41 2012 -0400 +++ b/agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java Mon Mar 19 19:44:09 2012 +0100 @@ -48,9 +48,9 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; -import com.redhat.thermostat.agent.config.StartupConfiguration; 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;
--- a/client/src/main/java/com/redhat/thermostat/client/Main.java Mon Mar 19 12:39:41 2012 -0400 +++ b/client/src/main/java/com/redhat/thermostat/client/Main.java Mon Mar 19 19:44:09 2012 +0100 @@ -53,6 +53,8 @@ import com.redhat.thermostat.client.ui.LayoutDebugHelper; import com.redhat.thermostat.client.ui.MainWindow; 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.Connection.ConnectionListener; import com.redhat.thermostat.common.dao.Connection.ConnectionStatus; @@ -78,7 +80,17 @@ System.exit(-1); } - ConnectionProvider connProv = new MongoConnectionProvider(arguments.getProperties()); + 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); + } + + ConnectionProvider connProv = new MongoConnectionProvider(config); DAOFactory daoFactory = new MongoDAOFactory(connProv); ApplicationContext.getInstance().setDAOFactory(daoFactory); }
--- a/common/pom.xml Mon Mar 19 12:39:41 2012 -0400 +++ b/common/pom.xml Mon Mar 19 19:44:09 2012 +0100 @@ -45,7 +45,7 @@ <artifactId>thermostat</artifactId> <version>0.2-SNAPSHOT</version> </parent> - + <artifactId>thermostat-common</artifactId> <packaging>jar</packaging> @@ -91,6 +91,19 @@ <artifactId>mongo-java-driver</artifactId> <version>2.7.3</version> </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-easymock</artifactId> + <version>1.4.11</version> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <version>3.1</version> + </dependency> </dependencies> + <properties> + <argLine>-XX:-UseSplitVerifier</argLine> + </properties> </project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/config/Defaults.java Mon Mar 19 19:44:09 2012 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.common.config; + +import java.util.logging.Level; + +final class Defaults { + + private Defaults() { + /* Should not be instantiated */ + } + + public static final Level LOGGING_LEVEL = Level.WARNING; + public static final String DATABASE_URI = "mongodb://127.0.0.1"; + public static final int MONGOS_PORT = 27517; + public static final int MONGOD_PORT = 27518; + public static final boolean LOCAL_MODE = false; // Default behaviour is to connect to cluster. + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/config/StartupConfiguration.java Mon Mar 19 19:44:09 2012 +0100 @@ -0,0 +1,281 @@ +/* + * 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.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 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; + } + } +}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/Connection.java Mon Mar 19 12:39:41 2012 -0400 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/Connection.java Mon Mar 19 19:44:09 2012 +0100 @@ -69,6 +69,7 @@ public enum ConnectionStatus { CONNECTED, + CONNECTING, FAILED_TO_CONNECT, DISCONNECTED, }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java Mon Mar 19 12:39:41 2012 -0400 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java Mon Mar 19 19:44:09 2012 +0100 @@ -36,20 +36,16 @@ package com.redhat.thermostat.common.dao; -import java.io.IOException; -import java.io.OutputStream; import java.net.UnknownHostException; -import java.util.Properties; import java.util.logging.Logger; import com.mongodb.DB; import com.mongodb.Mongo; import com.mongodb.MongoException; import com.mongodb.MongoURI; -import com.redhat.thermostat.common.Constants; import com.redhat.thermostat.common.NotImplementedException; +import com.redhat.thermostat.common.config.StartupConfiguration; import com.redhat.thermostat.common.storage.StorageConstants; -import com.redhat.thermostat.common.utils.LoggedExternalProcess; import com.redhat.thermostat.common.utils.LoggingUtils; public class MongoConnection extends Connection { @@ -57,31 +53,24 @@ private Mongo m = null; private DB db = null; - private boolean hasLocalAgent = false; - private Process localAgentProcess = null; - private Properties props; - - public MongoConnection(Properties props) { - this.props = props; + + private StartupConfiguration conf; + + private MongoConnection() { /* nothing to do */ } + + public MongoConnection(StartupConfiguration conf) { + this.conf = conf; } @Override public void connect() { try { - m = new Mongo(getMongoURI()); - db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME); + createConnection(); /* the mongo java driver does not ensure this connection is actually working */ - testConnection(db); - } catch (MongoException e) { - fireChanged(ConnectionStatus.FAILED_TO_CONNECT); - return; - } catch (UnknownHostException e) { - fireChanged(ConnectionStatus.FAILED_TO_CONNECT); - return; - } catch (LocalAgentException e) { - fireChanged(ConnectionStatus.FAILED_TO_CONNECT); - return; - } catch (NotImplementedException e) { + testConnection(); + } catch (MongoException | UnknownHostException | + NotImplementedException | IllegalArgumentException e) + { fireChanged(ConnectionStatus.FAILED_TO_CONNECT); return; } @@ -89,7 +78,12 @@ connected = true; } - private static void testConnection(DB db) { + private void createConnection() throws MongoException, UnknownHostException { + this.m = new Mongo(getMongoURI()); + this.db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME); + } + + private void testConnection() { db.getCollection("agent-config").getCount(); } @@ -97,9 +91,7 @@ MongoURI uri = null; switch (getType()) { case LOCAL: - startLocalAgent(); - uri = new MongoURI("mongodb://127.0.0.1:" - + props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT)); + uri = new MongoURI(conf.getDBUriString()); break; case REMOTE: throw new NotImplementedException("No mongo URI implemented for REMOTE."); @@ -109,27 +101,6 @@ return uri; } - private void startLocalAgent() throws LocalAgentException { - int status = 0; - try { - String agentScript = props.getProperty(Constants.CLIENT_PROPERTY_AGENT_LAUNCH_SCRIPT); - localAgentProcess = new LoggedExternalProcess(new String[] { agentScript, "--local" }).runAndReturnProcess(); - // Allow some time for things to get started. - try { - // TODO provide some UI feedback here instead of just seeming dead. - Thread.sleep(2000); - } catch (InterruptedException e) { - // ignore - } - } catch (IOException e) { - throw new LocalAgentException(); - } - if (status != 0) { - throw new LocalAgentException(); - } - hasLocalAgent = true; - } - public DB getDB() { return db; } @@ -139,29 +110,6 @@ if (m != null) { m.close(); } - if (hasLocalAgent) { - stopLocalAgent(); - } connected = false; } - - private void stopLocalAgent() { - // TODO this is currently using Agent's 'run until some data avail on stdin' hack. - // That hack will go away, at which point we will need another way to shut down. - OutputStream agentIn = localAgentProcess.getOutputStream(); - byte[] anything = { 0x04 }; - try { - agentIn.write(anything); - agentIn.flush(); - localAgentProcess.waitFor(); - } catch (IOException e) { - logger.warning("Error shutting down local agent."); - } catch (InterruptedException e) { - logger.warning("Interrupted waiting for local agent to shut down."); - } - } - - private static class LocalAgentException extends RuntimeException { - private static final long serialVersionUID = -4507363475778127729L; - } }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnectionProvider.java Mon Mar 19 12:39:41 2012 -0400 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnectionProvider.java Mon Mar 19 19:44:09 2012 +0100 @@ -36,19 +36,19 @@ package com.redhat.thermostat.common.dao; -import java.util.Properties; +import com.redhat.thermostat.common.config.StartupConfiguration; public class MongoConnectionProvider implements ConnectionProvider { - private Properties connectionProperties; + private StartupConfiguration configuration; - public MongoConnectionProvider(Properties connProps) { - connectionProperties = connProps; + public MongoConnectionProvider(StartupConfiguration configuration) { + this.configuration = configuration; } @Override public Connection createConnection() { - return new MongoConnection(connectionProperties); + return new MongoConnection(configuration); } }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoDAOFactory.java Mon Mar 19 12:39:41 2012 -0400 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoDAOFactory.java Mon Mar 19 19:44:09 2012 +0100 @@ -47,8 +47,9 @@ private Connection connection; public MongoDAOFactory(ConnectionProvider connProv) { - final MongoStorage mongoStorage = new MongoStorage(); + connection = connProv.createConnection(); + final MongoStorage mongoStorage = new MongoStorage(connection); connection.addListener(new ConnectionListener() { @Override
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/storage/ConnectionFailedException.java Mon Mar 19 19:44:09 2012 +0100 @@ -0,0 +1,41 @@ +/* + * 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.storage; + +public class ConnectionFailedException extends Exception { + +}
--- a/common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java Mon Mar 19 12:39:41 2012 -0400 +++ b/common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java Mon Mar 19 19:44:09 2012 +0100 @@ -50,9 +50,9 @@ import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; -import com.mongodb.Mongo; -import com.mongodb.MongoURI; import com.mongodb.WriteConcern; +import com.redhat.thermostat.common.dao.Connection; +import com.redhat.thermostat.common.dao.MongoConnection; /** * Implementation of the Storage interface that uses MongoDB to store the instrumentation data. @@ -61,6 +61,10 @@ */ public class MongoStorage extends Storage { + public MongoStorage(Connection connection) { + super(connection); + } + public static final String KEY_AGENT_ID = "agent-id"; public static final String SET_MODIFIER = "$set"; @@ -70,13 +74,9 @@ private UUID agentId = null; @Override - public void connect(String uri) throws UnknownHostException { - connect(new MongoURI(uri)); - } - - private void connect(MongoURI uri) throws UnknownHostException { - Mongo mongo = new Mongo(uri); - db = mongo.getDB(StorageConstants.THERMOSTAT_DB_NAME); + public void connect() throws ConnectionFailedException { + connection.connect(); + db = ((MongoConnection) connection).getDB(); } /**
--- a/common/src/main/java/com/redhat/thermostat/common/storage/Storage.java Mon Mar 19 12:39:41 2012 -0400 +++ b/common/src/main/java/com/redhat/thermostat/common/storage/Storage.java Mon Mar 19 19:44:09 2012 +0100 @@ -36,12 +36,19 @@ package com.redhat.thermostat.common.storage; -import java.net.UnknownHostException; import java.util.UUID; +import com.redhat.thermostat.common.dao.Connection; + public abstract class Storage { - public abstract void connect(String uri) throws UnknownHostException; + protected Connection connection; + + public Storage (Connection connection) { + this.connection = connection; + } + + public abstract void connect() throws ConnectionFailedException; public abstract void setAgentId(UUID id);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java Mon Mar 19 19:44:09 2012 +0100 @@ -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.common.dao; + +import java.util.concurrent.CountDownLatch; + +import junit.framework.Assert; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.redhat.thermostat.common.config.StartupConfiguration; +import com.redhat.thermostat.common.dao.Connection.ConnectionListener; +import com.redhat.thermostat.common.dao.Connection.ConnectionStatus; +import com.redhat.thermostat.common.dao.Connection.ConnectionType; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.powermock.api.easymock.PowerMock.createPartialMockAndInvokeDefaultConstructor; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(MongoConnection.class) +public class ConnectionTest { + + /** + * Test if the appropriate events are notified upon connection + * success + * @throws Exception + */ + @Test + public void testLocalConnection() throws Exception { + + // FIXME: We should not use this level of mocking, but this will be + // fixed when the Storage and Connection classes refactoring + // will be complete. + Connection connection = + createPartialMockAndInvokeDefaultConstructor(MongoConnection.class, + "createConnection", + "testConnection"); + final boolean [] testPassed = new boolean[1]; + testPassed[0] = false; + final CountDownLatch latch = new CountDownLatch(1); + + connection.addListener(new ConnectionListener() { + @Override + public void changed(ConnectionStatus newStatus) { + if (newStatus == ConnectionStatus.CONNECTING) { + // only release the latch when we are done + return; + } + if (newStatus == ConnectionStatus.CONNECTED) { + testPassed[0] = true; + } + latch.countDown(); + } + }); + connection.connect(); + + latch.await(); + Assert.assertTrue(testPassed[0]); + } + + /** + * Test if the connection fails given an invalid db url + */ + @Test + public void testInvalidLocalConnection() throws InterruptedException { + + StartupConfiguration conf = mock(StartupConfiguration.class); + when(conf.getDBUriString()).thenReturn("fluff"); + ConnectionProvider connProv = new MongoConnectionProvider(conf); + + Connection connection = connProv.createConnection(); + Assert.assertNotNull(connection); + + connection.setType(ConnectionType.LOCAL); + Assert.assertTrue(ConnectionType.LOCAL == connection.getType()); + + final boolean [] testPassed = new boolean[1]; + testPassed[0] = false; + final CountDownLatch latch = new CountDownLatch(1); + + connection.addListener(new ConnectionListener() { + @Override + public void changed(ConnectionStatus newStatus) { + if (newStatus == ConnectionStatus.CONNECTING) { + // only release the latch when we are done + return; + } + + if (newStatus == ConnectionStatus.FAILED_TO_CONNECT) { + testPassed[0] = true; + } + latch.countDown(); + } + }); + connection.connect(); + + latch.await(); + Assert.assertTrue(testPassed[0]); + } +}
--- a/common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java Mon Mar 19 12:39:41 2012 -0400 +++ b/common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java Mon Mar 19 19:44:09 2012 +0100 @@ -79,7 +79,7 @@ @Before public void setUp() { - storage = new MongoStorage(); + storage = new MongoStorage(null); BasicDBObject value1 = new BasicDBObject(); value1.put("key1", "test1"); @@ -112,7 +112,7 @@ @Test public void testCreateConnectionKey() { - MongoStorage mongoStorage = new MongoStorage(); + MongoStorage mongoStorage = new MongoStorage(null); Category category = new Category("testCreateConnectionKey"); ConnectionKey connKey = mongoStorage.createConnectionKey(category); assertNotNull(connKey);