# HG changeset patch # User Elliott Baron # Date 1392077330 18000 # Node ID 7caa7f9afb14d6ce9ed16ffcfb63a57fdaf2397c # Parent 869c5e163e7ab8a16cc80beb33e6e46933b1dac9 Separate storage command from agent-cli Backport of PR1608. We get an exception that the RMI registry port is already in use for running "thermostat storage --status". This happens because agent-core's activator creates a thread pool for JMX connections, which in turn creates the RMI registry. My proposed solution is to separate the storage command from the agent bundles, currently it resides in agent-cli. This patch creates a storage-cli bundle that houses StorageCommand and its related classes. There are then some modifications to the ServiceCommand in order to invoke the storage command without having a bundle dependency on storage-cli, which would also entail having to export StorageCommand. Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-December/009022.html PR1648 diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/pom.xml --- a/agent/cli/pom.xml Fri Dec 20 11:42:37 2013 +0100 +++ b/agent/cli/pom.xml Mon Feb 10 19:08:50 2014 -0500 @@ -127,7 +127,6 @@ com.redhat.thermostat.agent.cli.impl, com.redhat.thermostat.agent.cli.impl.locale, - com.redhat.thermostat.agent.cli.impl.db, <_nouses>true diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java Fri Dec 20 11:42:37 2013 +0100 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java Mon Feb 10 19:08:50 2014 -0500 @@ -41,13 +41,11 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; -import com.redhat.thermostat.agent.cli.impl.db.StorageCommand; import com.redhat.thermostat.common.ExitStatus; import com.redhat.thermostat.common.MultipleServiceTracker; import com.redhat.thermostat.common.MultipleServiceTracker.Action; import com.redhat.thermostat.common.cli.CommandRegistry; import com.redhat.thermostat.common.cli.CommandRegistryImpl; -import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.storage.core.WriterID; public class Activator implements BundleActivator { @@ -62,7 +60,6 @@ Class[] deps = new Class[] { ExitStatus.class, - CommonPaths.class, WriterID.class // agent app uses it }; tracker = new MultipleServiceTracker(context, deps, new Action() { @@ -70,11 +67,9 @@ @Override public void dependenciesAvailable(Map services) { ExitStatus exitStatus = (ExitStatus) services.get(ExitStatus.class.getName()); - CommonPaths paths = (CommonPaths) services.get(CommonPaths.class.getName()); WriterID writerID = (WriterID) services.get(WriterID.class.getName()); agentApplication = new AgentApplication(context, exitStatus, writerID); reg.registerCommand("service", new ServiceCommand(context)); - reg.registerCommand("storage", new StorageCommand(exitStatus, paths)); reg.registerCommand("agent", agentApplication); } diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/ServiceCommand.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/ServiceCommand.java Fri Dec 20 11:42:37 2013 +0100 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/ServiceCommand.java Mon Feb 10 19:08:50 2014 -0500 @@ -43,12 +43,11 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; -import com.redhat.thermostat.agent.cli.impl.db.StorageAlreadyRunningException; -import com.redhat.thermostat.agent.cli.impl.db.StorageCommand; import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.cli.AbstractCommand; +import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; import com.redhat.thermostat.common.tools.ApplicationState; @@ -68,6 +67,7 @@ private BundleContext context; private Launcher launcher; private boolean storageFailed = false; + private CommandContext cmdCtx; public ServiceCommand(BundleContext context) { this.context = context; @@ -77,6 +77,7 @@ @Override public void run(CommandContext ctx) throws CommandException { + cmdCtx = ctx; ServiceReference launcherRef = context.getServiceReference(Launcher.class); if (launcherRef == null) { throw new CommandException(translator.localize(LocaleResources.LAUNCHER_UNAVAILABLE)); @@ -96,32 +97,47 @@ launcher.run(storageStopArgs, false); context.ungetService(launcherRef); + cmdCtx = null; } @Override public void actionPerformed(ActionEvent actionEvent) { - if (actionEvent.getSource() instanceof StorageCommand) { - StorageCommand storage = (StorageCommand) actionEvent.getSource(); + if (actionEvent.getSource() instanceof AbstractStateNotifyingCommand) { + AbstractStateNotifyingCommand storage = (AbstractStateNotifyingCommand) actionEvent.getSource(); // Implementation detail: there is a single StorageCommand instance registered // as an OSGi service. We remove ourselves as listener so that we don't get // notified in the case that the command is invoked by some other means later. storage.getNotifier().removeActionListener(this); - switch (actionEvent.getActionId()) { - case START: - String dbUrl = storage.getConfiguration().getDBConnectionString(); - String[] agentArgs = new String[] {"agent", "-d", dbUrl}; - System.err.println(translator.localize(LocaleResources.STARTING_AGENT).getContents()); - launcher.run(agentArgs, false); - break; - case FAIL: - storageFailed = true; - Object payload = actionEvent.getPayload(); - if (payload instanceof StorageAlreadyRunningException) { - System.err.println(translator.localize(LocaleResources.STORAGE_ALREADY_RUNNING).getContents()); + + try { + switch (actionEvent.getActionId()) { + case START: + // Payload is connection URL + Object payload = actionEvent.getPayload(); + if (payload == null || !(payload instanceof String)) { + throw new CommandException(translator.localize(LocaleResources.UNEXPECTED_RESULT_STORAGE)); + } + String dbUrl = (String) payload; + String[] agentArgs = new String[] {"agent", "-d", dbUrl}; + cmdCtx.getConsole().getError().println(translator.localize(LocaleResources.STARTING_AGENT).getContents()); + launcher.run(agentArgs, false); + break; + case FAIL: + storageFailed = true; + // Payload is exception + payload = actionEvent.getPayload(); + if (payload == null || !(payload instanceof Exception)) { + throw new CommandException(translator.localize(LocaleResources.UNEXPECTED_RESULT_STORAGE)); + } + Exception ex = (Exception) payload; + cmdCtx.getConsole().getError().println(ex.getMessage()); + break; } - break; + } catch (CommandException e) { + cmdCtx.getConsole().getError().println(e.getMessage()); + } finally { + agentBarrier.release(); } - agentBarrier.release(); } } diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/DBConfig.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/DBConfig.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.agent.cli.impl.db; - - -/** - * Set of configuration options that the {@link StorageCommand} understands. - * Keys map to properties in $THERMOSTAT_HOME/storage/db.properties. - */ -public enum DBConfig { - - /** - * The bind IP address. - */ - BIND, - /** - * The port on which mongodb will be listening. - */ - PORT, - /** - * Weather or not to start mongodb with SSL enabled. - */ - SSL_ENABLE, - /** - * The PEM encoded SSL certificate + SSL key. - */ - SSL_PEM_FILE, - /** - * The passphrase for the encrypted SSL key. Only used if the private key was encrypted. - */ - SSL_KEY_PASSWORD, - -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/DBOptionParser.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/DBOptionParser.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.agent.cli.impl.db; - -import com.redhat.thermostat.common.cli.Arguments; -import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; -import com.redhat.thermostat.common.config.ThermostatOptionParser; -import com.redhat.thermostat.shared.config.InvalidConfigurationException; -import com.redhat.thermostat.shared.locale.Translate; - -public class DBOptionParser implements ThermostatOptionParser { - - private static final Translate translator = LocaleResources.createLocalizer(); - - private boolean quiet; - - private DBStartupConfiguration configuration; - - private Arguments args; - - private DBArgs serviceAction; - - private boolean dryRun; - - public DBOptionParser(DBStartupConfiguration configuration, Arguments args) { - this.args = args; - this.configuration = configuration; - } - - @Override - public void parse() throws InvalidConfigurationException { - - if (args.hasArgument(DBArgs.START.option)) { - serviceAction = DBArgs.START; - } else if (args.hasArgument(DBArgs.STOP.option)) { - serviceAction = DBArgs.STOP; - } else if (args.hasArgument(DBArgs.STATUS.option)) { - serviceAction = DBArgs.STATUS; - } else { - throw new InvalidConfigurationException(translator.localize(LocaleResources.COMMAND_STORAGE_ARGUMENT_REQUIRED)); - } - - if (args.hasArgument(DBArgs.DRY.option)) { - dryRun = true; - } - - if (args.hasArgument(DBArgs.QUIET.option)) { - quiet = true; - } - - // leave at the end, since it depends on the previous settings - String address = configuration.getBindIP(); - long port = configuration.getPort(); - configuration.setDBConnectionString("mongodb://" + address + ":" + port); - } - - public boolean isDryRun() { - return dryRun; - } - - public DBArgs getAction() { - return serviceAction; - } - - static enum DBArgs { - - DRY("dryRun"), - - HELP("help"), - - START("start"), - STOP("stop"), - - QUIET("quiet"), - - STATUS("status"); - - private String option; - - DBArgs(String option) { - this.option = option; - } - } - - public boolean isQuiet() { - return quiet; - } -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/DBStartupConfiguration.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/DBStartupConfiguration.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,194 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.agent.cli.impl.db; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Properties; - -import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; -import com.redhat.thermostat.shared.config.InvalidConfigurationException; -import com.redhat.thermostat.shared.locale.Translate; -import com.redhat.thermostat.storage.config.StartupConfiguration; - -public class DBStartupConfiguration implements StartupConfiguration { - - private static final Translate t = LocaleResources.createLocalizer(); - - private File dbPath; - private File logFile; - private File pidFile; - private boolean sslEnabled = false; - private File sslPemFile; - private String sslKeyPassphrase; - - private long localPort; - - private String dbConnectionString; - - private String ip; - - public DBStartupConfiguration(File systemProperties, File userProperties, - File dbPath, File logFile, File pidFile) throws InvalidConfigurationException { - this.dbPath = dbPath; - this.logFile = logFile; - this.pidFile = pidFile; - readAndSetProperties(systemProperties, userProperties); - } - - public File getDBPath() { - return dbPath; - } - - public File getLogFile() { - return logFile; - } - - public File getPidFile() { - return pidFile; - } - - public void setPort(long localPort) { - this.localPort = localPort; - } - - public long getPort() { - return localPort; - } - - 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; - } - - public boolean isSslEnabled() { - return sslEnabled; - } - - void setSslEnabled(boolean sslEnabled) { - this.sslEnabled = sslEnabled; - } - - /** - * - * @return The file containing the server certificate and the private key in PEM format or null - * if nothing was specified in $THERMOSTAT_HOME/storage/db.properties. - */ - public File getSslPemFile() { - return sslPemFile; - } - - void setSslPemFile(File sslPemFile) { - this.sslPemFile = sslPemFile; - } - - /** - * - * @return The passphrase for the encrypted server key or null if config was - * not set. - */ - public String getSslKeyPassphrase() { - return sslKeyPassphrase; - } - - void setSslKeyPassphrase(String sslKeyPassphrase) { - this.sslKeyPassphrase = sslKeyPassphrase; - } - - private void readAndSetProperties(File systemPropertiesFile, File userPropertiesFile) throws InvalidConfigurationException { - - Properties systemProperties = new Properties(); - try { - systemProperties.load(new FileInputStream(systemPropertiesFile)); - } catch (IOException e) { - throw new InvalidConfigurationException(/* "Could not find system configuration", */e); - } - - Properties properties = new Properties(systemProperties); - try { - properties.load(new FileInputStream(userPropertiesFile)); - } catch (IOException e) { - // that's fine. we will just rely on system properties - } - - readAndSetProperties(properties); - } - - private void readAndSetProperties(Properties properties) { - String port = properties.getProperty(DBConfig.PORT.name()); - if (port != null) { - int localPort = Integer.parseInt(port); - setPort(localPort); - } else { - throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_PROPERTY, DBConfig.PORT.toString())); - } - - String ip = properties.getProperty(DBConfig.BIND.name()); - if (ip != null) { - setBindIP(ip); - } else { - throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_PROPERTY, DBConfig.BIND.toString())); - } - - // optional config - String enableSSLConfig = properties.getProperty(DBConfig.SSL_ENABLE.name()); - setSslEnabled(Boolean.parseBoolean(enableSSLConfig)); - - String pemFile = properties.getProperty(DBConfig.SSL_PEM_FILE.name()); - if (pemFile != null) { - setSslPemFile(new File(pemFile)); - } - - String keyPassPhrase = properties.getProperty(DBConfig.SSL_KEY_PASSWORD.name()); - setSslKeyPassphrase(keyPassPhrase); - - } -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/MongoProcessRunner.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/MongoProcessRunner.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,312 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.agent.cli.impl.db; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -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.logging.Level; -import java.util.logging.Logger; - -import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; -import com.redhat.thermostat.common.tools.ApplicationException; -import com.redhat.thermostat.common.utils.LoggedExternalProcess; -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.service.process.UnixProcessUtilities; -import com.redhat.thermostat.shared.config.InvalidConfigurationException; -import com.redhat.thermostat.shared.locale.LocalizedString; -import com.redhat.thermostat.shared.locale.Translate; - -public class MongoProcessRunner { - - private static final Translate translator = LocaleResources.createLocalizer(); - private static final Logger logger = LoggingUtils.getLogger(MongoProcessRunner.class); - - private static final String MONGO_PROCESS = "mongod"; - - private static final String [] MONGO_BASIC_ARGS = { - "mongod", "--quiet", "--fork", "--auth", "--nohttpinterface", "--bind_ip" - }; - - private static final String [] MONGO_SHUTDOWN_ARGS = { - "kill", "-s", "TERM" - }; - - private static final String NO_JOURNAL_ARGUMENT = "--nojournal"; - static final String NO_JOURNAL_FIRST_VERSION = "1.9.2"; - - private DBStartupConfiguration configuration; - private boolean isQuiet; - private Integer pid; - - public MongoProcessRunner(DBStartupConfiguration configuration, boolean quiet) { - this.configuration = configuration; - this.isQuiet = quiet; - } - - private boolean checkPid() { - File pidfile = configuration.getPidFile(); - Charset charset = Charset.defaultCharset(); - if (pidfile.exists()) { - try (BufferedReader reader = Files.newBufferedReader(pidfile.toPath(), charset)) { - pid = doGetPid(reader); - } catch (IOException ex) { - logger.log(Level.WARNING, "Exception while reading pid file", ex); - pid = null; - } catch (NumberFormatException e) { - logger.log(Level.WARNING, "Mongo PID file does not contain a valid PID", e); - pid = null; - } - } else { - pid = null; - } - return (pid != null); - } - - // package private for testing - Integer doGetPid(BufferedReader reader) throws IOException { - String line = reader.readLine(); - // readLine() returns null on EOF - if (line == null || line.isEmpty()) { - return null; - } - else { - return Integer.parseInt(line); - } - } - - private void deleteStalePidFile() { - pid = null; - LocalizedString message = translator.localize(LocaleResources.STALE_PID_FILE_NO_MATCHING_PROCESS, configuration.getPidFile().toString(), MONGO_PROCESS); - // Mongo didn't remove its PID file? Work around the issue. Log - // the event, remove the stale pid file and continue. - logger.log(Level.WARNING, message.getContents()); - try { - Files.delete(configuration.getPidFile().toPath()); - } catch (IOException benign) { - // ignore this benign error - } - } - - public boolean isStorageRunning() { - if (!checkPid()) { - return false; - } - - String processName = UnixProcessUtilities.getInstance().getProcessName(pid); - // TODO: check if we want mongos or mongod from the configs - boolean processIsRunning = processName != null && processName.equalsIgnoreCase(MONGO_PROCESS); - if (!processIsRunning) { - deleteStalePidFile(); - } - return processIsRunning; - } - - /** - * Start the mongod process. - * - * @throws ApplicationException to signal an error starting the process. Callers should catch this and handle appropriately. - */ - public void startService() throws IOException, InterruptedException, - ApplicationException, InvalidConfigurationException { - - if (isStorageRunning()) { - LocalizedString message = translator.localize(LocaleResources.STORAGE_ALREADY_RUNNING_WITH_PID, String.valueOf(pid)); - throw new StorageAlreadyRunningException(pid, message.getContents()); - } - - String dbVersion; - try { - dbVersion = getDBVersion(); - } catch (IOException e) { - LocalizedString message = translator.localize( - LocaleResources.CANNOT_EXECUTE_PROCESS, MONGO_PROCESS); - throw new ApplicationException(message.getContents(), e); - - } - List commands = null; - commands = getStartupCommand(dbVersion); - - display(translator.localize(LocaleResources.STARTING_STORAGE_SERVER)); - - LoggedExternalProcess process = new LoggedExternalProcess(commands); - int status = -1; - try { - status = process.runAndReturnResult(); - } catch (ApplicationException ae) { - LocalizedString message = translator.localize(LocaleResources.CANNOT_EXECUTE_PROCESS, MONGO_PROCESS); - throw ae; - } - - Thread.sleep(500); - - if (status == 0) { - if (!isStorageRunning()) { - status = -1; - } - } - - if (status == 0) { - display(translator.localize(LocaleResources.SERVER_LISTENING_ON, configuration.getDBConnectionString())); - display(translator.localize(LocaleResources.LOG_FILE_AT, configuration.getLogFile().toString())); - display(translator.localize(LocaleResources.PID_IS, String.valueOf(pid))); - - } else { - // don't display anything when throwing an exception; whatever catches the exception will do so. - LocalizedString message = translator.localize(LocaleResources.CANNOT_START_SERVER, - configuration.getDBPath().toString(), - String.valueOf(status)); - throw new StorageStartException(configuration.getDBPath(), status, message.getContents()); - } - } - - /** - * Stop the mongod process. - * - * @throws ApplicationException to signal an error stopping the storage. Callers should catch this and handle appropriately. - */ - public void stopService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { - - if (!isStorageRunning()) { - LocalizedString message = translator.localize(LocaleResources.STORAGE_NOT_RUNNING); - throw new StorageNotRunningException(message.getContents()); - } - List commands = new ArrayList<>(Arrays.asList(MONGO_SHUTDOWN_ARGS)); - commands.add(String.valueOf(pid)); - - LoggedExternalProcess process = new LoggedExternalProcess(commands); - int status = process.runAndReturnResult(); - if (status == 0) { - display(translator.localize(LocaleResources.SERVER_SHUTDOWN_COMPLETE, configuration.getDBPath().toString())); - display(translator.localize(LocaleResources.LOG_FILE_AT, configuration.getLogFile().toString())); - // all went well, make sure to remove pid file. - try { - Files.delete(configuration.getPidFile().toPath()); - } catch (IOException e) { - // ignore - } - } else { - // don't display anything when throwing an exception; whatever catches the exception will do so. - LocalizedString message = translator.localize(LocaleResources.CANNOT_SHUTDOWN_SERVER, - configuration.getDBPath().toString(), - String.valueOf(status)); - throw new StorageStopException(configuration.getDBPath(), status, message.getContents()); - } - } - - List getStartupCommand(String dbVersion) throws IOException, InvalidConfigurationException { - List commands = new ArrayList<>(Arrays.asList(MONGO_BASIC_ARGS)); - - if (dbVersion.compareTo(NO_JOURNAL_FIRST_VERSION) >= 0) { - commands.add(1, NO_JOURNAL_ARGUMENT); - } - 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"); - commands.add(Long.toString(configuration.getPort())); - - if (configuration.isSslEnabled()) { - // check for configuration which has a chance of working :) - if (configuration.getSslPemFile() == null) { - throw new InvalidConfigurationException(translator.localize(LocaleResources.MISSING_PEM)); - } else if (configuration.getSslKeyPassphrase() == null) { - throw new InvalidConfigurationException(translator.localize(LocaleResources.MISSING_PASSPHRASE)); - } - commands.add("--sslOnNormalPorts"); - commands.add("--sslPEMKeyFile"); - commands.add(configuration.getSslPemFile().getCanonicalPath()); - commands.add("--sslPEMKeyPassword"); - commands.add(configuration.getSslKeyPassphrase()); - } - - return commands; - } - - private String getDBVersion() throws IOException { - Process process = new ProcessBuilder(Arrays.asList("mongod", "--version")).start(); - InputStream out = process.getInputStream(); - return doGetDBVersion(out); - } - - // package private for testing - String doGetDBVersion(InputStream in) throws IOException { - // Default to no-journal first version if we can't parse the version - // output for some reason. - String versionString = NO_JOURNAL_FIRST_VERSION; - String firstLine = null; - try(InputStreamReader reader = new InputStreamReader(in)) { - BufferedReader bufReader = new BufferedReader(reader); - firstLine = bufReader.readLine(); - int commaIdx = firstLine.indexOf(",", 12); - if (commaIdx != -1) { - versionString = firstLine.substring(12, commaIdx); - } else { - versionString = firstLine.substring(12); - } - } catch (Exception e) { - // catching Exception here in order to also catch potential NPEs or - // IndexOutOfBoundExceptions. If those conditions happen we fall - // back to the no journal first version. - logger.log(Level.WARNING, "Failed to parse mongodb version from: '" + - firstLine + "'. Assuming version " + NO_JOURNAL_FIRST_VERSION, e); - } - return versionString; - } - - private void display(LocalizedString message) { - if (!isQuiet) { - System.out.println(message.getContents()); - } - } -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StalePidFileException.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StalePidFileException.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ -package com.redhat.thermostat.agent.cli.impl.db; - -import java.io.File; - -import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; -import com.redhat.thermostat.common.tools.ApplicationException; -import com.redhat.thermostat.shared.locale.Translate; - -@SuppressWarnings("serial") -public class StalePidFileException extends ApplicationException { - - private static final Translate translator = LocaleResources.createLocalizer(); - - private final File pidFile; - - public StalePidFileException(File pidFile) { - super(translator.localize(LocaleResources.STALE_PID_FILE, pidFile.toString()).getContents()); - this.pidFile = pidFile; - } - - public File getPidFile() { - return pidFile; - } - -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageAlreadyRunningException.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageAlreadyRunningException.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ -package com.redhat.thermostat.agent.cli.impl.db; - -import com.redhat.thermostat.common.tools.ApplicationException; - -@SuppressWarnings("serial") -public class StorageAlreadyRunningException extends ApplicationException { - - private final int storagePid; - - public StorageAlreadyRunningException(int pid, String message) { - super(message); - storagePid = pid; - } - - public int getStoragePid() { - return storagePid; - } - -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageCommand.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageCommand.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.agent.cli.impl.db; - -import java.io.File; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; -import com.redhat.thermostat.common.ExitStatus; -import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; -import com.redhat.thermostat.common.cli.Arguments; -import com.redhat.thermostat.common.cli.CommandContext; -import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.tools.ApplicationException; -import com.redhat.thermostat.common.tools.ApplicationState; -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.shared.config.InvalidConfigurationException; -import com.redhat.thermostat.shared.locale.Translate; - -public class StorageCommand extends AbstractStateNotifyingCommand { - - private static final Logger logger = LoggingUtils.getLogger(StorageCommand.class); - private static final Translate t = LocaleResources.createLocalizer(); - - private DBStartupConfiguration configuration; - private DBOptionParser parser; - private final ExitStatus exitStatus; - private final CommonPaths paths; - - private MongoProcessRunner runner; - - public StorageCommand(ExitStatus exitStatus, CommonPaths paths) { - this.exitStatus = exitStatus; - this.paths = paths; - } - - private void parseArguments(Arguments args) throws InvalidConfigurationException { - File dbPath = paths.getUserStorageDirectory(); - File logFile = paths.getUserStorageLogFile(); - File pidFile = paths.getUserStoragePidFile(); - File systemPropertyFile = paths.getSystemStorageConfigurationFile(); - if (!systemPropertyFile.exists()) { - throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_DB_CONFIG, systemPropertyFile.toString())); - } - File userPropertyFile = paths.getUserStorageConfigurationFile(); - // read everything that is in the configs - this.configuration = new DBStartupConfiguration(systemPropertyFile, userPropertyFile, dbPath, logFile, pidFile); - parser = new DBOptionParser(configuration, args); - parser.parse(); - } - - @Override - public void run(CommandContext ctx) throws CommandException { - parseArgsAndRun(ctx); - } - - private void parseArgsAndRun(CommandContext ctx) - throws InvalidConfigurationException { - parseArguments(ctx.getArguments()); - - // dry run means we don't do anything at all - if (parser.isDryRun()) return; - - runner = createRunner(); - try { - switch (parser.getAction()) { - case START: - startService(); - break; - case STOP: - stopService(); - break; - case STATUS: - printServiceStatus(ctx); - break; - default: - break; - } - getNotifier().fireAction(ApplicationState.SUCCESS); - } catch (InvalidConfigurationException e) { - // rethrow - throw e; - } catch (ApplicationException e) { - logger.log(Level.WARNING, e.getMessage()); - getNotifier().fireAction(ApplicationState.FAIL, e); - } catch (Exception e) { - logger.log(Level.WARNING, e.getMessage(), e); - getNotifier().fireAction(ApplicationState.FAIL, e); - } - } - - private void startService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { - try { - createNeededDirectories(); - runner.startService(); - } catch (ApplicationException | InvalidConfigurationException | IOException e) { - // something went wrong set status appropriately. This makes sure - // that the JVM exits with this status. - exitStatus.setExitStatus(ExitStatus.EXIT_ERROR); - // rethrow - throw e; - } - getNotifier().fireAction(ApplicationState.START); - } - - private void createNeededDirectories() throws InvalidConfigurationException { - File[] requiredDirectories = new File[] { - configuration.getDBPath(), - }; - - for (File directory : requiredDirectories) { - if (!directory.isDirectory() && !directory.mkdirs()) { - throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_DB_DIR)); - } - } - - File[] requiredFiles = new File[] { - configuration.getLogFile(), - configuration.getPidFile(), - }; - - for (File file : requiredFiles) { - File directory = file.getParentFile(); - if (!directory.isDirectory() && !directory.mkdirs()) { - throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_DB_DIR)); - } - } - } - - private void stopService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { - try { - check(); - runner.stopService(); - } catch (ApplicationException | InvalidConfigurationException | InterruptedException | IOException e) { - // something went wrong set status appropriately. This makes sure - // that the JVM exits with this status. - exitStatus.setExitStatus(ExitStatus.EXIT_ERROR); - throw e; - } - getNotifier().fireAction(ApplicationState.STOP); - } - - private void check() throws InvalidConfigurationException { - if (!configuration.getDBPath().exists() || - !configuration.getLogFile().getParentFile().exists() || - !configuration.getPidFile().getParentFile().exists()) - { - throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_DB_DIR)); - } - } - - private void printServiceStatus(CommandContext ctx) { - if (runner.isStorageRunning()) { - ctx.getConsole().getOutput().println(t.localize(LocaleResources.STORAGE_RUNNING).getContents()); - } else { - ctx.getConsole().getOutput().println(t.localize(LocaleResources.STORAGE_NOT_RUNNING).getContents()); - } - } - - MongoProcessRunner createRunner() { - return new MongoProcessRunner(configuration, parser.isQuiet()); - } - - public DBStartupConfiguration getConfiguration() { - return configuration; - } - - @Override - public boolean isStorageRequired() { - return false; - } - -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageNotRunningException.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageNotRunningException.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.agent.cli.impl.db; - -import com.redhat.thermostat.common.tools.ApplicationException; - -@SuppressWarnings("serial") -public class StorageNotRunningException extends ApplicationException { - - public StorageNotRunningException(String message) { - super(message); - } -} diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageStartException.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageStartException.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ -package com.redhat.thermostat.agent.cli.impl.db; - -import java.io.File; - -import com.redhat.thermostat.common.tools.ApplicationException; - -@SuppressWarnings("serial") -public class StorageStartException extends ApplicationException { - - private final File dbFile; - private final int status; - - public StorageStartException(File dbPath, int status, String message) { - super(message); - this.dbFile = dbPath; - this.status = status; - } - - public File getDbPath() { - return dbFile; - } - - public int getStatus() { - return status; - } - -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageStopException.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/db/StorageStopException.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ -package com.redhat.thermostat.agent.cli.impl.db; - -import java.io.File; - -import com.redhat.thermostat.common.tools.ApplicationException; - -@SuppressWarnings("serial") -public class StorageStopException extends ApplicationException { - - private final File dbConfig; - private final int status; - - public StorageStopException(File dbConfig, int status, String message) { - super(message); - this.dbConfig = dbConfig; - this.status = status; - } - - public File getDbConfig() { - return dbConfig; - } - - public int getStatus() { - return status; - } - -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/locale/LocaleResources.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/locale/LocaleResources.java Fri Dec 20 11:42:37 2013 +0100 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/locale/LocaleResources.java Mon Feb 10 19:08:50 2014 -0500 @@ -42,29 +42,9 @@ STARTING_AGENT, - COMMAND_STORAGE_ARGUMENT_REQUIRED, - - STORAGE_ALREADY_RUNNING, - STORAGE_ALREADY_RUNNING_WITH_PID, SERVICE_FAILED_TO_START_DB, - SERVER_SHUTDOWN_COMPLETE, - LOG_FILE_AT, - CANNOT_START_SERVER, - CANNOT_SHUTDOWN_SERVER, - STALE_PID_FILE, - STALE_PID_FILE_NO_MATCHING_PROCESS, - STARTING_STORAGE_SERVER, - CANNOT_EXECUTE_PROCESS, - SERVER_LISTENING_ON, - PID_IS, LAUNCHER_UNAVAILABLE, - MISSING_PROPERTY, - MISSING_PEM, - MISSING_PASSPHRASE, - MISSING_DB_CONFIG, - MISSING_DB_DIR, - STORAGE_RUNNING, - STORAGE_NOT_RUNNING + UNEXPECTED_RESULT_STORAGE, ; static final String RESOURCE_BUNDLE = "com.redhat.thermostat.agent.cli.impl.strings"; diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/impl/strings.properties --- a/agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/impl/strings.properties Fri Dec 20 11:42:37 2013 +0100 +++ b/agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/impl/strings.properties Mon Feb 10 19:08:50 2014 -0500 @@ -1,25 +1,5 @@ STARTING_AGENT = starting agent now... -COMMAND_STORAGE_ARGUMENT_REQUIRED = either --start or --stop must be given - SERVICE_FAILED_TO_START_DB = Service failed to start due to error starting storage. -STORAGE_ALREADY_RUNNING = Storage is already running. Please use "agent --start" to start the agent -STORAGE_ALREADY_RUNNING_WITH_PID = Storage is already running with pid {0} -SERVER_SHUTDOWN_COMPLETE = server shutdown complete: {0} -LOG_FILE_AT = log file is here: {0} -CANNOT_START_SERVER = cannot start server {0}, exit status: {1}. Please check that your configuration is valid -CANNOT_SHUTDOWN_SERVER = cannot shutdown server {0}, exit status: {1}. Please check that your configuration is valid -STALE_PID_FILE = stale pid file: {0} -STALE_PID_FILE_NO_MATCHING_PROCESS = A stale pid file ({0}) is present but there is no matching {1} process. Removing stale pid file. -STARTING_STORAGE_SERVER = starting storage server... -CANNOT_EXECUTE_PROCESS = can not execute {0} process. is it installed? -SERVER_LISTENING_ON = server listening on ip: {0} -PID_IS = pid: {0} LAUNCHER_UNAVAILABLE = Launcher is not available -MISSING_PROPERTY = {0} property missing -MISSING_PEM = No SSL PEM file specified! -MISSING_PASSPHRASE = No SSL key passphrase set! -MISSING_DB_CONFIG = can't access database configuration file {0} -MISSING_DB_DIR = database directories do not exist... -STORAGE_RUNNING = Storage is running -STORAGE_NOT_RUNNING = Storage is not running +UNEXPECTED_RESULT_STORAGE = Unexpected result from storage. diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ActivatorTest.java --- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ActivatorTest.java Fri Dec 20 11:42:37 2013 +0100 +++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ActivatorTest.java Mon Feb 10 19:08:50 2014 -0500 @@ -42,9 +42,7 @@ import org.junit.Test; -import com.redhat.thermostat.agent.cli.impl.db.StorageCommand; import com.redhat.thermostat.common.ExitStatus; -import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.storage.core.WriterID; import com.redhat.thermostat.testutils.StubBundleContext; @@ -56,10 +54,8 @@ ExitStatus exitStatus = mock(ExitStatus.class); WriterID writerID = mock(WriterID.class); - CommonPaths paths = mock(CommonPaths.class); bundleContext.registerService(WriterID.class, writerID, null); bundleContext.registerService(ExitStatus.class, exitStatus, null); - bundleContext.registerService(CommonPaths.class, paths, null); Activator activator = new Activator(); @@ -67,16 +63,15 @@ activator.start(bundleContext); - assertEquals(3, bundleContext.getServiceListeners().size()); + assertEquals(2, bundleContext.getServiceListeners().size()); assertCommandIsRegistered(bundleContext, "agent", AgentApplication.class); assertCommandIsRegistered(bundleContext, "service", ServiceCommand.class); - assertCommandIsRegistered(bundleContext, "storage", StorageCommand.class); activator.stop(bundleContext); assertEquals(0, bundleContext.getServiceListeners().size()); - assertEquals(3, bundleContext.getAllServices().size()); + assertEquals(2, bundleContext.getAllServices().size()); } } diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ServiceCommandTest.java --- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ServiceCommandTest.java Fri Dec 20 11:42:37 2013 +0100 +++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ServiceCommandTest.java Mon Feb 10 19:08:50 2014 -0500 @@ -36,16 +36,17 @@ package com.redhat.thermostat.agent.cli.impl; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.isA; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.never; +import java.io.PrintStream; import java.util.Collection; import junit.framework.Assert; @@ -55,15 +56,14 @@ import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.osgi.framework.ServiceReference; -import com.redhat.thermostat.agent.cli.impl.db.DBStartupConfiguration; -import com.redhat.thermostat.agent.cli.impl.db.StorageCommand; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.Console; import com.redhat.thermostat.common.tools.ApplicationState; import com.redhat.thermostat.launcher.Launcher; import com.redhat.thermostat.testutils.StubBundleContext; @@ -91,16 +91,18 @@ bundleContext.registerService(Launcher.class, mockLauncher, null); serviceCommand = new ServiceCommand(bundleContext); - StorageCommand mockStorageCommand = mock(StorageCommand.class); + AbstractStateNotifyingCommand mockStorageCommand = mock(AbstractStateNotifyingCommand.class); mockActionEvent = mock(ActionEvent.class); when(mockActionEvent.getSource()).thenReturn(mockStorageCommand); mockCommandContext = mock(CommandContext.class); + Console console = mock(Console.class); + PrintStream err = mock(PrintStream.class); + when(console.getError()).thenReturn(err); + when(mockCommandContext.getConsole()).thenReturn(console); ActionNotifier mockNotifier = mock(ActionNotifier.class); - DBStartupConfiguration mockDbsConfiguration = mock(DBStartupConfiguration.class); when(mockStorageCommand.getNotifier()).thenReturn(mockNotifier); - when(mockStorageCommand.getConfiguration()).thenReturn(mockDbsConfiguration); - when(mockDbsConfiguration.getDBConnectionString()).thenReturn(new String("Test String")); + when(mockActionEvent.getPayload()).thenReturn(new String("Test String")); } @After @@ -156,6 +158,7 @@ when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START); } else { when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL); + when(mockActionEvent.getPayload()).thenReturn(new Exception("Test Exception")); } ++count; @@ -197,6 +200,7 @@ listeners = (Collection>)args[1]; when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL); + when(mockActionEvent.getPayload()).thenReturn(new Exception("Test Exception")); for(ActionListener listener : listeners) { listener.actionPerformed(mockActionEvent); diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/db/DBStartupConfigurationTest.java --- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/db/DBStartupConfigurationTest.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.agent.cli.impl.db; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import java.io.File; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.redhat.thermostat.shared.config.InvalidConfigurationException; - -public class DBStartupConfigurationTest { - - private File dbLogFile; - private File dbPidFile; - private File dbPath; - - @Before - public void setUp() { - dbLogFile = new File("db.log"); - dbPidFile = new File("db.pid"); - dbPath = new File("somepath"); - } - - @After - public void tearDown() { - dbLogFile = null; - dbPidFile = null; - dbPath = null; - } - - @Test - public void canGetConfigFromPropertiesFile() throws Exception { - File dbProps = new File(this.getClass().getResource("/testDbConfig.properties").getFile()); - File canNotBeFoundFile = new File(""); - DBStartupConfiguration dbConfig = new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile); - - assertEquals(dbLogFile.getAbsolutePath(), dbConfig.getLogFile().getAbsolutePath()); - assertEquals(dbPidFile.getAbsolutePath(), dbConfig.getPidFile().getAbsolutePath()); - assertEquals(dbPath.getAbsolutePath(), dbConfig.getDBPath().getAbsolutePath()); - assertEquals("127.0.0.1", dbConfig.getBindIP()); - assertEquals(27518, dbConfig.getPort()); - assertEquals(true, dbConfig.isSslEnabled()); - assertEquals("/path/to/some/pem/file.pem", dbConfig.getSslPemFile().getAbsolutePath()); - assertEquals("somepassword", dbConfig.getSslKeyPassphrase()); - } - - @Test - public void canGetConfigFromPropertiesFile2() throws Exception { - File dbProps = new File(this.getClass().getResource("/testDbConfig2.properties").getFile()); - File canNotBeFoundFile = new File(""); - DBStartupConfiguration dbConfig = new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile); - - assertEquals(dbLogFile.getAbsolutePath(), dbConfig.getLogFile().getAbsolutePath()); - assertEquals(dbPidFile.getAbsolutePath(), dbConfig.getPidFile().getAbsolutePath()); - assertEquals(dbPath.getAbsolutePath(), dbConfig.getDBPath().getAbsolutePath()); - assertEquals("127.0.0.1", dbConfig.getBindIP()); - assertEquals(27518, dbConfig.getPort()); - assertEquals(false, dbConfig.isSslEnabled()); - assertNull(dbConfig.getSslPemFile()); - assertNull(dbConfig.getSslKeyPassphrase()); - } - - @Test - public void missingBindThrowsConfigException() throws Exception { - File dbProps = new File(this.getClass().getResource("/brokenDbConfig.properties").getFile()); - File canNotBeFoundFile = new File(""); - try { - new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile); - fail("BIND was not specified in properties file"); - } catch (InvalidConfigurationException e) { - assertEquals("BIND property missing", e.getMessage()); - } - } - - @Test - public void missingPortThrowsConfigException() throws Exception { - File dbProps = new File(this.getClass().getResource("/brokenDbConfig2.properties").getFile()); - File canNotBeFoundFile = new File(""); - try { - new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile); - fail("PORT was not specified in properties file"); - } catch (InvalidConfigurationException e) { - assertEquals("PORT property missing", e.getMessage()); - } - } -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/db/MongoProcessRunnerTest.java --- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/db/MongoProcessRunnerTest.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -package com.redhat.thermostat.agent.cli.impl.db; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.redhat.thermostat.agent.cli.impl.db.MongoProcessRunner; -import com.redhat.thermostat.shared.config.InvalidConfigurationException; - -public class MongoProcessRunnerTest { - - private MongoProcessRunner runner; - private DBStartupConfiguration config; - private static final String NO_JOURNAL_MONGODB_VERSION = "2.0.0"; - private static final String JOURNAL_MONGODB_VERSION = "1.8.0"; - private static final String BIND_IP = "127.0.0.1"; - private static final long PORT = 12456; - - @Before - public void setUp() { - File dbPath = new File("/path/to/db"); - File logPath = new File("/path/to/log"); - File pidFile = new File("/path/to/pid"); - config = mock(DBStartupConfiguration.class); - when(config.getBindIP()).thenReturn(BIND_IP); - when(config.getPort()).thenReturn(PORT); - when(config.getDBPath()).thenReturn(dbPath); - when(config.getLogFile()).thenReturn(logPath); - when(config.getPidFile()).thenReturn(pidFile); - runner = new MongoProcessRunner(config, false); - } - - @After - public void tearDown() { - runner = null; - config = null; - } - - @Test - public void testCommandArgumentsWithJournalVersion() throws Exception { - String[] expected = { "mongod", "--nojournal", "--quiet", "--fork", - "--auth", "--nohttpinterface", "--bind_ip", config.getBindIP(), - "--dbpath", config.getDBPath().getCanonicalPath(), "--logpath", - config.getLogFile().getCanonicalPath(), "--pidfilepath", - config.getPidFile().getCanonicalPath(), "--port", - Long.toString(config.getPort()) }; - List cmds = runner.getStartupCommand(NO_JOURNAL_MONGODB_VERSION); - String[] actual = cmds.toArray(new String[0]); - verifyEquals(expected, actual); - } - - @Test - public void testCommandArgumentsWithNoJournalVersion() throws Exception { - String[] expected = { "mongod", "--quiet", "--fork", "--auth", - "--nohttpinterface", "--bind_ip", config.getBindIP(), - "--dbpath", config.getDBPath().getCanonicalPath(), "--logpath", - config.getLogFile().getCanonicalPath(), "--pidfilepath", - config.getPidFile().getCanonicalPath(), "--port", - Long.toString(config.getPort()) }; - List cmds = runner.getStartupCommand(JOURNAL_MONGODB_VERSION); - String[] actual = cmds.toArray(new String[0]); - verifyEquals(expected, actual); - } - - @Test - public void testCommandArgumentsWithSSLEnabled() throws Exception { - when(config.isSslEnabled()).thenReturn(true); - File pemFile = new File("/path/to/cert_and_key.pem"); - when(config.getSslPemFile()).thenReturn(pemFile); - when(config.getSslKeyPassphrase()).thenReturn("non-null"); - String[] expected = { "mongod", "--quiet", "--fork", "--auth", - "--nohttpinterface", "--bind_ip", config.getBindIP(), - "--dbpath", config.getDBPath().getCanonicalPath(), "--logpath", - config.getLogFile().getCanonicalPath(), "--pidfilepath", - config.getPidFile().getCanonicalPath(), "--port", - Long.toString(config.getPort()), "--sslOnNormalPorts", - "--sslPEMKeyFile", config.getSslPemFile().getCanonicalPath(), - "--sslPEMKeyPassword", config.getSslKeyPassphrase() - }; - List cmds = runner.getStartupCommand(JOURNAL_MONGODB_VERSION); - String[] actual = cmds.toArray(new String[0]); - verifyEquals(expected, actual); - } - - @Test - public void testCommandArgumentsWithSSLEnabledThrowsInvalidConfigException() throws IOException { - when(config.isSslEnabled()).thenReturn(true); - // PEM file can't be null when SSL == true - when(config.getSslPemFile()).thenReturn(null); - try { - runner.getStartupCommand(JOURNAL_MONGODB_VERSION); - fail("Should have thrown exception!"); - } catch (InvalidConfigurationException e) { - assertEquals("No SSL PEM file specified!", e.getMessage()); - } - // Key password can't be null when SSL == true and keyfile present - File pemFile = new File("/path/to/ssl.pem"); - when(config.getSslPemFile()).thenReturn(pemFile); - when(config.getSslKeyPassphrase()).thenReturn(null); - try { - runner.getStartupCommand(JOURNAL_MONGODB_VERSION); - fail("Should have thrown exception!"); - } catch (InvalidConfigurationException e) { - assertEquals("No SSL key passphrase set!", e.getMessage()); - } - } - - @Test - public void testCheckPidNull() throws IOException { - BufferedReader reader = mock(BufferedReader.class); - when(reader.readLine()).thenReturn(null); - Integer pid = runner.doGetPid(reader); - assertNull(pid); - } - - @Test - public void canGetVersionFromVersionCmdOutputV22() throws IOException { - String versionOutput = "db version v2.2.4, pdfile version 4.5\n" + - "Mon Aug 26 17:13:45 git version: nogitversion"; - ByteArrayInputStream in = new ByteArrayInputStream(versionOutput.getBytes()); - String version = runner.doGetDBVersion(in); - assertEquals("2.2.4", version); - } - - @Test - public void canGetVersionFromVersionCmdOutputV24() throws IOException { - String versionOutput = "db version v2.4.5\n" + - "Mon Aug 26 18:01:28.404 git version: nogitversion"; - ByteArrayInputStream in = new ByteArrayInputStream(versionOutput.getBytes()); - String version = runner.doGetDBVersion(in); - assertEquals("2.4.5", version); - } - - @Test - public void canProceedIfGetDbVersionThrowsException() throws IOException { - String versionOutput = "foo\n" + - "Mon Aug 26 18:01:28.404 git version: nogitversion"; - ByteArrayInputStream in = new ByteArrayInputStream(versionOutput.getBytes()); - String version = runner.doGetDBVersion(in); - assertEquals(MongoProcessRunner.NO_JOURNAL_FIRST_VERSION, version); - } - - private void verifyEquals(String[] expected, String[] actual) { - assertEquals(expected.length, actual.length); - for (int i=0; i < expected.length; i++) { - assertEquals(expected[i], actual[i]); - } - } -} diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/db/StorageCommandTest.java --- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/db/StorageCommandTest.java Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,332 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.agent.cli.impl.db; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.IOException; -import java.util.Properties; -import java.util.concurrent.CountDownLatch; - -import junit.framework.Assert; - -import org.junit.After; -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.ExitStatus; -import com.redhat.thermostat.common.cli.CommandContext; -import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.cli.SimpleArguments; -import com.redhat.thermostat.common.tools.ApplicationException; -import com.redhat.thermostat.common.tools.ApplicationState; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.shared.config.InvalidConfigurationException; -import com.redhat.thermostat.shared.config.internal.CommonPathsImpl; -import com.redhat.thermostat.testutils.TestUtils; - -public class StorageCommandTest { - - private static final String PORT = "27518"; - private static final String BIND = "127.0.0.1"; - private static final String DB = "data/db"; - - private String tmpDir; - private ExitStatus exitStatus; - private CommonPaths paths; - - @Before - public void setup() { - exitStatus = mock(ExitStatus.class); - // need to create a dummy config file for the test - try { - Properties props = new Properties(); - - props.setProperty(DBConfig.BIND.name(), BIND); - props.setProperty(DBConfig.PORT.name(), PORT); - - tmpDir = TestUtils.setupStorageConfigs(props); - } catch (IOException e) { - Assert.fail("cannot setup tests: " + e); - } - - paths = mock(CommonPathsImpl.class); - File baseDir = new File(tmpDir); - File userRuntimeDir = new File(baseDir, "run"); - File userDataDir = new File(baseDir, "data"); - File logsDir = new File(baseDir, "logs"); - File confDir = new File(baseDir, "etc"); - - when(paths.getUserThermostatHome()).thenReturn(baseDir); - when(paths.getUserRuntimeDataDirectory()).thenReturn(userRuntimeDir); - when(paths.getUserPersistentDataDirectory()).thenReturn(userDataDir); - when(paths.getUserConfigurationDirectory()).thenReturn(confDir); - when(paths.getUserStorageDirectory()).thenCallRealMethod(); - when(paths.getUserStorageConfigurationFile()).thenCallRealMethod(); - when(paths.getUserLogDirectory()).thenReturn(logsDir); - when(paths.getUserStorageLogFile()).thenCallRealMethod(); - when(paths.getUserStoragePidFile()).thenCallRealMethod(); - - when(paths.getSystemThermostatHome()).thenReturn(baseDir); - when(paths.getSystemConfigurationDirectory()).thenCallRealMethod(); - when(paths.getSystemStorageConfigurationFile()).thenCallRealMethod(); - } - - @After - public void tearDown() { - exitStatus = null; - } - - @Test - public void testConfig() throws CommandException { - SimpleArguments args = new SimpleArguments(); - args.addArgument("quiet", null); - args.addArgument("start", null); - args.addArgument("dryRun", null); - CommandContext ctx = mock(CommandContext.class); - when(ctx.getArguments()).thenReturn(args); - - StorageCommand service = new StorageCommand(exitStatus, paths) { - @Override - MongoProcessRunner createRunner() { - throw new AssertionError("dry run should never create an actual runner"); - } - }; - - service.run(ctx); - - DBStartupConfiguration conf = service.getConfiguration(); - - Assert.assertEquals(tmpDir + DB, conf.getDBPath().getPath()); - Assert.assertEquals(Integer.parseInt(PORT), conf.getPort()); - Assert.assertEquals("mongodb://" + BIND + ":" + PORT , conf.getDBConnectionString()); - } - - private StorageCommand prepareService(boolean startSuccess) throws IOException, - InterruptedException, InvalidConfigurationException, ApplicationException - { - final MongoProcessRunner runner = mock(MongoProcessRunner.class); - if (!startSuccess) { - doThrow(new ApplicationException("mock exception")).when(runner).startService(); - } - - // TODO: stop not tested yet, but be sure it's not called from the code - doThrow(new ApplicationException("mock exception")).when(runner).stopService(); - - StorageCommand service = new StorageCommand(exitStatus, paths) { - @Override - MongoProcessRunner createRunner() { - return runner; - } - }; - - return service; - } - - @Test - public void testListeners() throws InterruptedException, IOException, ApplicationException, InvalidConfigurationException, CommandException - { - StorageCommand service = prepareService(true); - - final CountDownLatch latch = new CountDownLatch(2); - - final boolean[] result = new boolean[2]; - service.getNotifier().addActionListener(new ActionListener() { - @SuppressWarnings("incomplete-switch") - @Override - public void actionPerformed(ActionEvent actionEvent) { - switch (actionEvent.getActionId()) { - case FAIL: - result[0] = false; - latch.countDown(); - latch.countDown(); - break; - - case SUCCESS: - result[0] = true; - latch.countDown(); - break; - - case START: - result[1] = true; - latch.countDown(); - break; - } - } - }); - - service.run(prepareContext()); - latch.await(); - - Assert.assertTrue(result[0]); - Assert.assertTrue(result[1]); - } - - @Test - public void testListenersFail() throws InterruptedException, IOException, ApplicationException, CommandException, InvalidConfigurationException - { - StorageCommand service = prepareService(false); - - final CountDownLatch latch = new CountDownLatch(1); - final boolean[] result = new boolean[1]; - service.getNotifier().addActionListener(new ActionListener() { - @SuppressWarnings("incomplete-switch") - @Override - public void actionPerformed(ActionEvent actionEvent) { - switch (actionEvent.getActionId()) { - case FAIL: - result[0] = true; - break; - - case SUCCESS: - result[0] = false; - break; - } - latch.countDown(); - } - }); - - service.run(prepareContext()); - latch.await(); - - Assert.assertTrue(result[0]); - } - - @Test - public void exceptionSetsExitStatusOnFailure() throws Exception { - this.exitStatus = new ExitStatus() { - - private int exitStatus = -1; - - @Override - public void setExitStatus(int newExitStatus) { - exitStatus = newExitStatus; - } - - @Override - public int getExitStatus() { - return exitStatus; - } - }; - assertEquals(-1, this.exitStatus.getExitStatus()); - StorageCommand command = prepareService(false); - final CountDownLatch latch = new CountDownLatch(1); - final boolean[] result = new boolean[1]; - command.getNotifier().addActionListener(new ActionListener() { - @SuppressWarnings("incomplete-switch") - @Override - public void actionPerformed(ActionEvent actionEvent) { - switch (actionEvent.getActionId()) { - case FAIL: - result[0] = true; - break; - - case SUCCESS: - result[0] = false; - break; - } - latch.countDown(); - } - }); - command.run(prepareContext()); - latch.await(); - // should have failed - assertTrue(result[0]); - assertEquals(ExitStatus.EXIT_ERROR, this.exitStatus.getExitStatus()); - } - - @Test - public void exitStatusRemainsUntouchedOnSuccess() throws Exception { - this.exitStatus = new ExitStatus() { - - private int exitStatus = -1; - - @Override - public void setExitStatus(int newExitStatus) { - exitStatus = newExitStatus; - } - - @Override - public int getExitStatus() { - return exitStatus; - } - }; - StorageCommand command = prepareService(true); - final CountDownLatch latch = new CountDownLatch(1); - final boolean[] result = new boolean[1]; - command.getNotifier().addActionListener(new ActionListener() { - @SuppressWarnings("incomplete-switch") - @Override - public void actionPerformed(ActionEvent actionEvent) { - switch (actionEvent.getActionId()) { - case FAIL: - result[0] = false; - break; - - case SUCCESS: - result[0] = true; - break; - } - latch.countDown(); - } - }); - command.run(prepareContext()); - latch.await(); - // should have worked - assertTrue(result[0]); - // this impl of ExitStatus has a default value of -1 - assertEquals(-1, this.exitStatus.getExitStatus()); - } - - private CommandContext prepareContext() { - SimpleArguments args = new SimpleArguments(); - args.addArgument("quiet", "--quiet"); - args.addArgument("start", "--start"); - CommandContext ctx = mock(CommandContext.class); - when(ctx.getArguments()).thenReturn(args); - return ctx; - } - -} - diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/resources/brokenDbConfig.properties --- a/agent/cli/src/test/resources/brokenDbConfig.properties Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -PORT=27518 -# missing BIND \ No newline at end of file diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/resources/brokenDbConfig2.properties --- a/agent/cli/src/test/resources/brokenDbConfig2.properties Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -#PORT=27518 -BIND=127.0.0.1 \ No newline at end of file diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/resources/testDbConfig.properties --- a/agent/cli/src/test/resources/testDbConfig.properties Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -PORT=27518 -BIND=127.0.0.1 - -## SSL config fluff -SSL_ENABLE=true -SSL_PEM_FILE=/path/to/some/pem/file.pem -SSL_KEY_PASSWORD=somepassword \ No newline at end of file diff -r 869c5e163e7a -r 7caa7f9afb14 agent/cli/src/test/resources/testDbConfig2.properties --- a/agent/cli/src/test/resources/testDbConfig2.properties Fri Dec 20 11:42:37 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -PORT=27518 -BIND=127.0.0.1 - -## SSL config fluff -#SSL_ENABLE=true -#SSL_PEM_FILE=/path/to/some/pem/file.pem -#SSL_KEY_PASSWORD=somepassword \ No newline at end of file diff -r 869c5e163e7a -r 7caa7f9afb14 distribution/assembly/core-assembly.xml --- a/distribution/assembly/core-assembly.xml Fri Dec 20 11:42:37 2013 +0100 +++ b/distribution/assembly/core-assembly.xml Mon Feb 10 19:08:50 2014 -0500 @@ -66,6 +66,7 @@ com.redhat.thermostat:thermostat-common-core com.redhat.thermostat:thermostat-common-command com.redhat.thermostat:thermostat-osgi-process-handler + com.redhat.thermostat:thermostat-storage-cli com.redhat.thermostat:thermostat-keyring com.redhat.thermostat:thermostat-web-client com.redhat.thermostat:thermostat-web-cmd diff -r 869c5e163e7a -r 7caa7f9afb14 distribution/config/commands/service.properties --- a/distribution/config/commands/service.properties Fri Dec 20 11:42:37 2013 +0100 +++ b/distribution/config/commands/service.properties Mon Feb 10 19:08:50 2014 -0500 @@ -8,6 +8,7 @@ com.redhat.thermostat.process=${project.version}, \ com.redhat.thermostat.common.command=${project.version}, \ com.redhat.thermostat.agent.command=${project.version}, \ + com.redhat.thermostat.storage.cli=${project.version}, \ com.redhat.thermostat.agent.cli=${project.version}, \ com.redhat.thermostat.agent.proxy.common=${project.version}, \ org.jboss.netty=${netty.version} diff -r 869c5e163e7a -r 7caa7f9afb14 distribution/config/commands/storage.properties --- a/distribution/config/commands/storage.properties Fri Dec 20 11:42:37 2013 +0100 +++ b/distribution/config/commands/storage.properties Mon Feb 10 19:08:50 2014 -0500 @@ -1,9 +1,6 @@ -bundles = com.redhat.thermostat.agent.core=${project.version}, \ - com.redhat.thermostat.process=${project.version}, \ - com.redhat.thermostat.agent.cli=${project.version}, \ +bundles = com.redhat.thermostat.process=${project.version}, \ com.redhat.thermostat.common.command=${project.version}, \ - com.redhat.thermostat.agent.command=${project.version}, \ - com.redhat.thermostat.agent.proxy.common=${project.version}, \ + com.redhat.thermostat.storage.cli=${project.version}, \ org.apache.commons.codec=${commons-codec.osgi-version}, \ org.jboss.netty=${netty.version} diff -r 869c5e163e7a -r 7caa7f9afb14 distribution/pom.xml --- a/distribution/pom.xml Fri Dec 20 11:42:37 2013 +0100 +++ b/distribution/pom.xml Mon Feb 10 19:08:50 2014 -0500 @@ -309,6 +309,11 @@ com.redhat.thermostat + thermostat-storage-cli + ${project.version} + + + com.redhat.thermostat thermostat-agent-command ${project.version} diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/pom.xml Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,110 @@ + + + + 4.0.0 + + + com.redhat.thermostat + thermostat-storage + 1.0.1-SNAPSHOT + + + thermostat-storage-cli + bundle + + Thermostat Storage Command Line + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + com.redhat.thermostat + thermostat-osgi-process-handler + ${project.version} + + + org.osgi + org.osgi.core + + + com.redhat.thermostat + thermostat-common-test + ${project.version} + test + + + com.redhat.thermostat + thermostat-storage-core + ${project.version} + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + Red Hat, Inc. + com.redhat.thermostat.storage.cli.internal.Activator + com.redhat.thermostat.storage.cli + + com.redhat.thermostat.storage.cli.internal, + com.redhat.thermostat.storage.cli.internal.locale, + + + <_nouses>true + + + + + + + + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/Activator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/Activator.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,87 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import java.util.Map; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +import com.redhat.thermostat.common.ExitStatus; +import com.redhat.thermostat.common.MultipleServiceTracker; +import com.redhat.thermostat.common.MultipleServiceTracker.Action; +import com.redhat.thermostat.common.cli.CommandRegistry; +import com.redhat.thermostat.common.cli.CommandRegistryImpl; +import com.redhat.thermostat.shared.config.CommonPaths; + +public class Activator implements BundleActivator { + + private CommandRegistry reg; + private MultipleServiceTracker tracker; + + @Override + public void start(final BundleContext context) throws Exception { + reg = new CommandRegistryImpl(context); + + Class[] deps = new Class[] { + ExitStatus.class, + CommonPaths.class + }; + tracker = new MultipleServiceTracker(context, deps, new Action() { + + @Override + public void dependenciesAvailable(Map services) { + ExitStatus exitStatus = (ExitStatus) services.get(ExitStatus.class.getName()); + CommonPaths paths = (CommonPaths) services.get(CommonPaths.class.getName()); + reg.registerCommand("storage", new StorageCommand(exitStatus, paths)); + } + + @Override + public void dependenciesUnavailable() { + reg.unregisterCommands(); + } + }); + tracker.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + reg.unregisterCommands(); + tracker.close(); + } +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/DBConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/DBConfig.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,68 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + + +/** + * Set of configuration options that the {@link StorageCommand} understands. + * Keys map to properties in $THERMOSTAT_HOME/storage/db.properties. + */ +public enum DBConfig { + + /** + * The bind IP address. + */ + BIND, + /** + * The port on which mongodb will be listening. + */ + PORT, + /** + * Weather or not to start mongodb with SSL enabled. + */ + SSL_ENABLE, + /** + * The PEM encoded SSL certificate + SSL key. + */ + SSL_PEM_FILE, + /** + * The passphrase for the encrypted SSL key. Only used if the private key was encrypted. + */ + SSL_KEY_PASSWORD, + +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/DBOptionParser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/DBOptionParser.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,123 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.config.ThermostatOptionParser; +import com.redhat.thermostat.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.cli.internal.locale.LocaleResources; + +public class DBOptionParser implements ThermostatOptionParser { + + private static final Translate translator = LocaleResources.createLocalizer(); + + private boolean quiet; + + private DBStartupConfiguration configuration; + + private Arguments args; + + private DBArgs serviceAction; + + private boolean dryRun; + + public DBOptionParser(DBStartupConfiguration configuration, Arguments args) { + this.args = args; + this.configuration = configuration; + } + + @Override + public void parse() throws InvalidConfigurationException { + + if (args.hasArgument(DBArgs.START.option)) { + serviceAction = DBArgs.START; + } else if (args.hasArgument(DBArgs.STOP.option)) { + serviceAction = DBArgs.STOP; + } else if (args.hasArgument(DBArgs.STATUS.option)) { + serviceAction = DBArgs.STATUS; + } else { + throw new InvalidConfigurationException(translator.localize(LocaleResources.COMMAND_STORAGE_ARGUMENT_REQUIRED)); + } + + if (args.hasArgument(DBArgs.DRY.option)) { + dryRun = true; + } + + if (args.hasArgument(DBArgs.QUIET.option)) { + quiet = true; + } + + // leave at the end, since it depends on the previous settings + String address = configuration.getBindIP(); + long port = configuration.getPort(); + configuration.setDBConnectionString("mongodb://" + address + ":" + port); + } + + public boolean isDryRun() { + return dryRun; + } + + public DBArgs getAction() { + return serviceAction; + } + + static enum DBArgs { + + DRY("dryRun"), + + HELP("help"), + + START("start"), + STOP("stop"), + + QUIET("quiet"), + + STATUS("status"); + + private String option; + + DBArgs(String option) { + this.option = option; + } + } + + public boolean isQuiet() { + return quiet; + } +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/DBStartupConfiguration.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/DBStartupConfiguration.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,194 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +import com.redhat.thermostat.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.cli.internal.locale.LocaleResources; +import com.redhat.thermostat.storage.config.StartupConfiguration; + +public class DBStartupConfiguration implements StartupConfiguration { + + private static final Translate t = LocaleResources.createLocalizer(); + + private File dbPath; + private File logFile; + private File pidFile; + private boolean sslEnabled = false; + private File sslPemFile; + private String sslKeyPassphrase; + + private long localPort; + + private String dbConnectionString; + + private String ip; + + public DBStartupConfiguration(File systemProperties, File userProperties, + File dbPath, File logFile, File pidFile) throws InvalidConfigurationException { + this.dbPath = dbPath; + this.logFile = logFile; + this.pidFile = pidFile; + readAndSetProperties(systemProperties, userProperties); + } + + public File getDBPath() { + return dbPath; + } + + public File getLogFile() { + return logFile; + } + + public File getPidFile() { + return pidFile; + } + + public void setPort(long localPort) { + this.localPort = localPort; + } + + public long getPort() { + return localPort; + } + + 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; + } + + public boolean isSslEnabled() { + return sslEnabled; + } + + void setSslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + } + + /** + * + * @return The file containing the server certificate and the private key in PEM format or null + * if nothing was specified in $THERMOSTAT_HOME/storage/db.properties. + */ + public File getSslPemFile() { + return sslPemFile; + } + + void setSslPemFile(File sslPemFile) { + this.sslPemFile = sslPemFile; + } + + /** + * + * @return The passphrase for the encrypted server key or null if config was + * not set. + */ + public String getSslKeyPassphrase() { + return sslKeyPassphrase; + } + + void setSslKeyPassphrase(String sslKeyPassphrase) { + this.sslKeyPassphrase = sslKeyPassphrase; + } + + private void readAndSetProperties(File systemPropertiesFile, File userPropertiesFile) throws InvalidConfigurationException { + + Properties systemProperties = new Properties(); + try { + systemProperties.load(new FileInputStream(systemPropertiesFile)); + } catch (IOException e) { + throw new InvalidConfigurationException(/* "Could not find system configuration", */e); + } + + Properties properties = new Properties(systemProperties); + try { + properties.load(new FileInputStream(userPropertiesFile)); + } catch (IOException e) { + // that's fine. we will just rely on system properties + } + + readAndSetProperties(properties); + } + + private void readAndSetProperties(Properties properties) { + String port = properties.getProperty(DBConfig.PORT.name()); + if (port != null) { + int localPort = Integer.parseInt(port); + setPort(localPort); + } else { + throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_PROPERTY, DBConfig.PORT.toString())); + } + + String ip = properties.getProperty(DBConfig.BIND.name()); + if (ip != null) { + setBindIP(ip); + } else { + throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_PROPERTY, DBConfig.BIND.toString())); + } + + // optional config + String enableSSLConfig = properties.getProperty(DBConfig.SSL_ENABLE.name()); + setSslEnabled(Boolean.parseBoolean(enableSSLConfig)); + + String pemFile = properties.getProperty(DBConfig.SSL_PEM_FILE.name()); + if (pemFile != null) { + setSslPemFile(new File(pemFile)); + } + + String keyPassPhrase = properties.getProperty(DBConfig.SSL_KEY_PASSWORD.name()); + setSslKeyPassphrase(keyPassPhrase); + + } +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/MongoProcessRunner.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/MongoProcessRunner.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,312 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +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.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.common.tools.ApplicationException; +import com.redhat.thermostat.common.utils.LoggedExternalProcess; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.service.process.UnixProcessUtilities; +import com.redhat.thermostat.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.shared.locale.LocalizedString; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.cli.internal.locale.LocaleResources; + +public class MongoProcessRunner { + + private static final Translate translator = LocaleResources.createLocalizer(); + private static final Logger logger = LoggingUtils.getLogger(MongoProcessRunner.class); + + private static final String MONGO_PROCESS = "mongod"; + + private static final String [] MONGO_BASIC_ARGS = { + "mongod", "--quiet", "--fork", "--auth", "--nohttpinterface", "--bind_ip" + }; + + private static final String [] MONGO_SHUTDOWN_ARGS = { + "kill", "-s", "TERM" + }; + + private static final String NO_JOURNAL_ARGUMENT = "--nojournal"; + static final String NO_JOURNAL_FIRST_VERSION = "1.9.2"; + + private DBStartupConfiguration configuration; + private boolean isQuiet; + private Integer pid; + + public MongoProcessRunner(DBStartupConfiguration configuration, boolean quiet) { + this.configuration = configuration; + this.isQuiet = quiet; + } + + private boolean checkPid() { + File pidfile = configuration.getPidFile(); + Charset charset = Charset.defaultCharset(); + if (pidfile.exists()) { + try (BufferedReader reader = Files.newBufferedReader(pidfile.toPath(), charset)) { + pid = doGetPid(reader); + } catch (IOException ex) { + logger.log(Level.WARNING, "Exception while reading pid file", ex); + pid = null; + } catch (NumberFormatException e) { + logger.log(Level.WARNING, "Mongo PID file does not contain a valid PID", e); + pid = null; + } + } else { + pid = null; + } + return (pid != null); + } + + // package private for testing + Integer doGetPid(BufferedReader reader) throws IOException { + String line = reader.readLine(); + // readLine() returns null on EOF + if (line == null || line.isEmpty()) { + return null; + } + else { + return Integer.parseInt(line); + } + } + + private void deleteStalePidFile() { + pid = null; + LocalizedString message = translator.localize(LocaleResources.STALE_PID_FILE_NO_MATCHING_PROCESS, configuration.getPidFile().toString(), MONGO_PROCESS); + // Mongo didn't remove its PID file? Work around the issue. Log + // the event, remove the stale pid file and continue. + logger.log(Level.WARNING, message.getContents()); + try { + Files.delete(configuration.getPidFile().toPath()); + } catch (IOException benign) { + // ignore this benign error + } + } + + public boolean isStorageRunning() { + if (!checkPid()) { + return false; + } + + String processName = UnixProcessUtilities.getInstance().getProcessName(pid); + // TODO: check if we want mongos or mongod from the configs + boolean processIsRunning = processName != null && processName.equalsIgnoreCase(MONGO_PROCESS); + if (!processIsRunning) { + deleteStalePidFile(); + } + return processIsRunning; + } + + /** + * Start the mongod process. + * + * @throws ApplicationException to signal an error starting the process. Callers should catch this and handle appropriately. + */ + public void startService() throws IOException, InterruptedException, + ApplicationException, InvalidConfigurationException { + + if (isStorageRunning()) { + LocalizedString message = translator.localize(LocaleResources.STORAGE_ALREADY_RUNNING_WITH_PID, String.valueOf(pid)); + throw new StorageAlreadyRunningException(pid, message.getContents()); + } + + String dbVersion; + try { + dbVersion = getDBVersion(); + } catch (IOException e) { + LocalizedString message = translator.localize( + LocaleResources.CANNOT_EXECUTE_PROCESS, MONGO_PROCESS); + throw new ApplicationException(message.getContents(), e); + + } + List commands = null; + commands = getStartupCommand(dbVersion); + + display(translator.localize(LocaleResources.STARTING_STORAGE_SERVER)); + + LoggedExternalProcess process = new LoggedExternalProcess(commands); + int status = -1; + try { + status = process.runAndReturnResult(); + } catch (ApplicationException ae) { + LocalizedString message = translator.localize(LocaleResources.CANNOT_EXECUTE_PROCESS, MONGO_PROCESS); + throw new ApplicationException(message.getContents(), ae); + } + + Thread.sleep(500); + + if (status == 0) { + if (!isStorageRunning()) { + status = -1; + } + } + + if (status == 0) { + display(translator.localize(LocaleResources.SERVER_LISTENING_ON, configuration.getDBConnectionString())); + display(translator.localize(LocaleResources.LOG_FILE_AT, configuration.getLogFile().toString())); + display(translator.localize(LocaleResources.PID_IS, String.valueOf(pid))); + + } else { + // don't display anything when throwing an exception; whatever catches the exception will do so. + LocalizedString message = translator.localize(LocaleResources.CANNOT_START_SERVER, + configuration.getDBPath().toString(), + String.valueOf(status)); + throw new StorageStartException(configuration.getDBPath(), status, message.getContents()); + } + } + + /** + * Stop the mongod process. + * + * @throws ApplicationException to signal an error stopping the storage. Callers should catch this and handle appropriately. + */ + public void stopService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { + + if (!isStorageRunning()) { + LocalizedString message = translator.localize(LocaleResources.STORAGE_NOT_RUNNING); + throw new StorageNotRunningException(message.getContents()); + } + List commands = new ArrayList<>(Arrays.asList(MONGO_SHUTDOWN_ARGS)); + commands.add(String.valueOf(pid)); + + LoggedExternalProcess process = new LoggedExternalProcess(commands); + int status = process.runAndReturnResult(); + if (status == 0) { + display(translator.localize(LocaleResources.SERVER_SHUTDOWN_COMPLETE, configuration.getDBPath().toString())); + display(translator.localize(LocaleResources.LOG_FILE_AT, configuration.getLogFile().toString())); + // all went well, make sure to remove pid file. + try { + Files.delete(configuration.getPidFile().toPath()); + } catch (IOException e) { + // ignore + } + } else { + // don't display anything when throwing an exception; whatever catches the exception will do so. + LocalizedString message = translator.localize(LocaleResources.CANNOT_SHUTDOWN_SERVER, + configuration.getDBPath().toString(), + String.valueOf(status)); + throw new StorageStopException(configuration.getDBPath(), status, message.getContents()); + } + } + + List getStartupCommand(String dbVersion) throws IOException, InvalidConfigurationException { + List commands = new ArrayList<>(Arrays.asList(MONGO_BASIC_ARGS)); + + if (dbVersion.compareTo(NO_JOURNAL_FIRST_VERSION) >= 0) { + commands.add(1, NO_JOURNAL_ARGUMENT); + } + 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"); + commands.add(Long.toString(configuration.getPort())); + + if (configuration.isSslEnabled()) { + // check for configuration which has a chance of working :) + if (configuration.getSslPemFile() == null) { + throw new InvalidConfigurationException(translator.localize(LocaleResources.MISSING_PEM)); + } else if (configuration.getSslKeyPassphrase() == null) { + throw new InvalidConfigurationException(translator.localize(LocaleResources.MISSING_PASSPHRASE)); + } + commands.add("--sslOnNormalPorts"); + commands.add("--sslPEMKeyFile"); + commands.add(configuration.getSslPemFile().getCanonicalPath()); + commands.add("--sslPEMKeyPassword"); + commands.add(configuration.getSslKeyPassphrase()); + } + + return commands; + } + + private String getDBVersion() throws IOException { + Process process = new ProcessBuilder(Arrays.asList("mongod", "--version")).start(); + InputStream out = process.getInputStream(); + return doGetDBVersion(out); + } + + // package private for testing + String doGetDBVersion(InputStream in) throws IOException { + // Default to no-journal first version if we can't parse the version + // output for some reason. + String versionString = NO_JOURNAL_FIRST_VERSION; + String firstLine = null; + try(InputStreamReader reader = new InputStreamReader(in)) { + BufferedReader bufReader = new BufferedReader(reader); + firstLine = bufReader.readLine(); + int commaIdx = firstLine.indexOf(",", 12); + if (commaIdx != -1) { + versionString = firstLine.substring(12, commaIdx); + } else { + versionString = firstLine.substring(12); + } + } catch (Exception e) { + // catching Exception here in order to also catch potential NPEs or + // IndexOutOfBoundExceptions. If those conditions happen we fall + // back to the no journal first version. + logger.log(Level.WARNING, "Failed to parse mongodb version from: '" + + firstLine + "'. Assuming version " + NO_JOURNAL_FIRST_VERSION, e); + } + return versionString; + } + + private void display(LocalizedString message) { + if (!isQuiet) { + System.out.println(message.getContents()); + } + } +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StalePidFileException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StalePidFileException.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,61 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import java.io.File; + +import com.redhat.thermostat.common.tools.ApplicationException; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.cli.internal.locale.LocaleResources; + +@SuppressWarnings("serial") +public class StalePidFileException extends ApplicationException { + + private static final Translate translator = LocaleResources.createLocalizer(); + + private final File pidFile; + + public StalePidFileException(File pidFile) { + super(translator.localize(LocaleResources.STALE_PID_FILE, pidFile.toString()).getContents()); + this.pidFile = pidFile; + } + + public File getPidFile() { + return pidFile; + } + +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageAlreadyRunningException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageAlreadyRunningException.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,55 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import com.redhat.thermostat.common.tools.ApplicationException; + +@SuppressWarnings("serial") +public class StorageAlreadyRunningException extends ApplicationException { + + private final int storagePid; + + public StorageAlreadyRunningException(int pid, String message) { + super(message); + storagePid = pid; + } + + public int getStoragePid() { + return storagePid; + } + +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageCommand.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageCommand.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,211 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.common.ExitStatus; +import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.tools.ApplicationException; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.shared.config.CommonPaths; +import com.redhat.thermostat.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.cli.internal.locale.LocaleResources; + +public class StorageCommand extends AbstractStateNotifyingCommand { + + private static final Logger logger = LoggingUtils.getLogger(StorageCommand.class); + private static final Translate t = LocaleResources.createLocalizer(); + + private DBStartupConfiguration configuration; + private DBOptionParser parser; + private final ExitStatus exitStatus; + private final CommonPaths paths; + + private MongoProcessRunner runner; + + public StorageCommand(ExitStatus exitStatus, CommonPaths paths) { + this.exitStatus = exitStatus; + this.paths = paths; + } + + private void parseArguments(Arguments args) throws InvalidConfigurationException { + File dbPath = paths.getUserStorageDirectory(); + File logFile = paths.getUserStorageLogFile(); + File pidFile = paths.getUserStoragePidFile(); + File systemPropertyFile = paths.getSystemStorageConfigurationFile(); + if (!systemPropertyFile.exists()) { + throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_DB_CONFIG, systemPropertyFile.toString())); + } + File userPropertyFile = paths.getUserStorageConfigurationFile(); + // read everything that is in the configs + this.configuration = new DBStartupConfiguration(systemPropertyFile, userPropertyFile, dbPath, logFile, pidFile); + parser = new DBOptionParser(configuration, args); + parser.parse(); + } + + @Override + public void run(CommandContext ctx) throws CommandException { + parseArgsAndRun(ctx); + } + + private void parseArgsAndRun(CommandContext ctx) + throws InvalidConfigurationException { + parseArguments(ctx.getArguments()); + + // dry run means we don't do anything at all + if (parser.isDryRun()) return; + + runner = createRunner(); + try { + switch (parser.getAction()) { + case START: + startService(); + break; + case STOP: + stopService(); + break; + case STATUS: + printServiceStatus(ctx); + break; + default: + break; + } + getNotifier().fireAction(ApplicationState.SUCCESS, configuration.getDBConnectionString()); + } catch (InvalidConfigurationException e) { + // rethrow + throw e; + } catch (ApplicationException e) { + logger.log(Level.WARNING, e.getMessage()); + getNotifier().fireAction(ApplicationState.FAIL, e); + } catch (Exception e) { + logger.log(Level.WARNING, e.getMessage(), e); + getNotifier().fireAction(ApplicationState.FAIL, e); + } + } + + private void startService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { + try { + createNeededDirectories(); + runner.startService(); + } catch (ApplicationException | InvalidConfigurationException | IOException e) { + // something went wrong set status appropriately. This makes sure + // that the JVM exits with this status. + exitStatus.setExitStatus(ExitStatus.EXIT_ERROR); + // rethrow + throw e; + } + getNotifier().fireAction(ApplicationState.START, configuration.getDBConnectionString()); + } + + private void createNeededDirectories() throws InvalidConfigurationException { + File[] requiredDirectories = new File[] { + configuration.getDBPath(), + }; + + for (File directory : requiredDirectories) { + if (!directory.isDirectory() && !directory.mkdirs()) { + throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_DB_DIR)); + } + } + + File[] requiredFiles = new File[] { + configuration.getLogFile(), + configuration.getPidFile(), + }; + + for (File file : requiredFiles) { + File directory = file.getParentFile(); + if (!directory.isDirectory() && !directory.mkdirs()) { + throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_DB_DIR)); + } + } + } + + private void stopService() throws IOException, InterruptedException, InvalidConfigurationException, ApplicationException { + try { + check(); + runner.stopService(); + } catch (ApplicationException | InvalidConfigurationException | InterruptedException | IOException e) { + // something went wrong set status appropriately. This makes sure + // that the JVM exits with this status. + exitStatus.setExitStatus(ExitStatus.EXIT_ERROR); + throw e; + } + getNotifier().fireAction(ApplicationState.STOP); + } + + private void check() throws InvalidConfigurationException { + if (!configuration.getDBPath().exists() || + !configuration.getLogFile().getParentFile().exists() || + !configuration.getPidFile().getParentFile().exists()) + { + throw new InvalidConfigurationException(t.localize(LocaleResources.MISSING_DB_DIR)); + } + } + + private void printServiceStatus(CommandContext ctx) { + if (runner.isStorageRunning()) { + ctx.getConsole().getOutput().println(t.localize(LocaleResources.STORAGE_RUNNING).getContents()); + } else { + ctx.getConsole().getOutput().println(t.localize(LocaleResources.STORAGE_NOT_RUNNING).getContents()); + } + } + + MongoProcessRunner createRunner() { + return new MongoProcessRunner(configuration, parser.isQuiet()); + } + + public DBStartupConfiguration getConfiguration() { + return configuration; + } + + @Override + public boolean isStorageRequired() { + return false; + } + +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageNotRunningException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageNotRunningException.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,47 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import com.redhat.thermostat.common.tools.ApplicationException; + +@SuppressWarnings("serial") +public class StorageNotRunningException extends ApplicationException { + + public StorageNotRunningException(String message) { + super(message); + } +} diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageStartException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageStartException.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,63 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import java.io.File; + +import com.redhat.thermostat.common.tools.ApplicationException; + +@SuppressWarnings("serial") +public class StorageStartException extends ApplicationException { + + private final File dbFile; + private final int status; + + public StorageStartException(File dbPath, int status, String message) { + super(message); + this.dbFile = dbPath; + this.status = status; + } + + public File getDbPath() { + return dbFile; + } + + public int getStatus() { + return status; + } + +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageStopException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/StorageStopException.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,63 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import java.io.File; + +import com.redhat.thermostat.common.tools.ApplicationException; + +@SuppressWarnings("serial") +public class StorageStopException extends ApplicationException { + + private final File dbConfig; + private final int status; + + public StorageStopException(File dbConfig, int status, String message) { + super(message); + this.dbConfig = dbConfig; + this.status = status; + } + + public File getDbConfig() { + return dbConfig; + } + + public int getStatus() { + return status; + } + +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/locale/LocaleResources.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/locale/LocaleResources.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,72 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal.locale; + +import com.redhat.thermostat.shared.locale.Translate; + +public enum LocaleResources { + + COMMAND_STORAGE_ARGUMENT_REQUIRED, + + STORAGE_ALREADY_RUNNING_WITH_PID, + SERVER_SHUTDOWN_COMPLETE, + LOG_FILE_AT, + CANNOT_START_SERVER, + CANNOT_SHUTDOWN_SERVER, + STALE_PID_FILE, + STALE_PID_FILE_NO_MATCHING_PROCESS, + STARTING_STORAGE_SERVER, + CANNOT_EXECUTE_PROCESS, + SERVER_LISTENING_ON, + PID_IS, + MISSING_PROPERTY, + MISSING_PEM, + MISSING_PASSPHRASE, + MISSING_DB_CONFIG, + MISSING_DB_DIR, + STORAGE_RUNNING, + STORAGE_NOT_RUNNING + ; + + static final String RESOURCE_BUNDLE = "com.redhat.thermostat.storage.cli.internal.strings"; + + public static Translate createLocalizer() { + return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); + } + +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/main/resources/com/redhat/thermostat/storage/cli/internal/strings.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/resources/com/redhat/thermostat/storage/cli/internal/strings.properties Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,20 @@ +COMMAND_STORAGE_ARGUMENT_REQUIRED = either --start or --stop must be given + +STORAGE_ALREADY_RUNNING_WITH_PID = Storage is already running with pid {0} +SERVER_SHUTDOWN_COMPLETE = server shutdown complete: {0} +LOG_FILE_AT = log file is here: {0} +CANNOT_START_SERVER = cannot start server {0}, exit status: {1}. Please check that your configuration is valid +CANNOT_SHUTDOWN_SERVER = cannot shutdown server {0}, exit status: {1}. Please check that your configuration is valid +STALE_PID_FILE = stale pid file: {0} +STALE_PID_FILE_NO_MATCHING_PROCESS = A stale pid file ({0}) is present but there is no matching {1} process. Removing stale pid file. +STARTING_STORAGE_SERVER = starting storage server... +CANNOT_EXECUTE_PROCESS = can not execute {0} process. is it installed? +SERVER_LISTENING_ON = server listening on ip: {0} +PID_IS = pid: {0} +MISSING_PROPERTY = {0} property missing +MISSING_PEM = No SSL PEM file specified! +MISSING_PASSPHRASE = No SSL key passphrase set! +MISSING_DB_CONFIG = can't access database configuration file {0} +MISSING_DB_DIR = database directories do not exist... +STORAGE_RUNNING = Storage is running +STORAGE_NOT_RUNNING = Storage is not running diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/ActivatorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/ActivatorTest.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,76 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import com.redhat.thermostat.common.ExitStatus; +import com.redhat.thermostat.shared.config.CommonPaths; +import com.redhat.thermostat.testutils.StubBundleContext; + +public class ActivatorTest { + + @Test + public void verifyActivatorRegistersCommands() throws Exception { + StubBundleContext bundleContext = new StubBundleContext(); + + ExitStatus exitStatus = mock(ExitStatus.class); + CommonPaths paths = mock(CommonPaths.class); + bundleContext.registerService(ExitStatus.class, exitStatus, null); + bundleContext.registerService(CommonPaths.class, paths, null); + + Activator activator = new Activator(); + + assertEquals(0, bundleContext.getServiceListeners().size()); + + activator.start(bundleContext); + + assertEquals(2, bundleContext.getServiceListeners().size()); + + assertCommandIsRegistered(bundleContext, "storage", StorageCommand.class); + + activator.stop(bundleContext); + + assertEquals(0, bundleContext.getServiceListeners().size()); + assertEquals(2, bundleContext.getAllServices().size()); + } +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/DBStartupConfigurationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/DBStartupConfigurationTest.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,128 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.io.File; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.storage.cli.internal.DBStartupConfiguration; + +public class DBStartupConfigurationTest { + + private File dbLogFile; + private File dbPidFile; + private File dbPath; + + @Before + public void setUp() { + dbLogFile = new File("db.log"); + dbPidFile = new File("db.pid"); + dbPath = new File("somepath"); + } + + @After + public void tearDown() { + dbLogFile = null; + dbPidFile = null; + dbPath = null; + } + + @Test + public void canGetConfigFromPropertiesFile() throws Exception { + File dbProps = new File(this.getClass().getResource("/testDbConfig.properties").getFile()); + File canNotBeFoundFile = new File(""); + DBStartupConfiguration dbConfig = new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile); + + assertEquals(dbLogFile.getAbsolutePath(), dbConfig.getLogFile().getAbsolutePath()); + assertEquals(dbPidFile.getAbsolutePath(), dbConfig.getPidFile().getAbsolutePath()); + assertEquals(dbPath.getAbsolutePath(), dbConfig.getDBPath().getAbsolutePath()); + assertEquals("127.0.0.1", dbConfig.getBindIP()); + assertEquals(27518, dbConfig.getPort()); + assertEquals(true, dbConfig.isSslEnabled()); + assertEquals("/path/to/some/pem/file.pem", dbConfig.getSslPemFile().getAbsolutePath()); + assertEquals("somepassword", dbConfig.getSslKeyPassphrase()); + } + + @Test + public void canGetConfigFromPropertiesFile2() throws Exception { + File dbProps = new File(this.getClass().getResource("/testDbConfig2.properties").getFile()); + File canNotBeFoundFile = new File(""); + DBStartupConfiguration dbConfig = new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile); + + assertEquals(dbLogFile.getAbsolutePath(), dbConfig.getLogFile().getAbsolutePath()); + assertEquals(dbPidFile.getAbsolutePath(), dbConfig.getPidFile().getAbsolutePath()); + assertEquals(dbPath.getAbsolutePath(), dbConfig.getDBPath().getAbsolutePath()); + assertEquals("127.0.0.1", dbConfig.getBindIP()); + assertEquals(27518, dbConfig.getPort()); + assertEquals(false, dbConfig.isSslEnabled()); + assertNull(dbConfig.getSslPemFile()); + assertNull(dbConfig.getSslKeyPassphrase()); + } + + @Test + public void missingBindThrowsConfigException() throws Exception { + File dbProps = new File(this.getClass().getResource("/brokenDbConfig.properties").getFile()); + File canNotBeFoundFile = new File(""); + try { + new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile); + fail("BIND was not specified in properties file"); + } catch (InvalidConfigurationException e) { + assertEquals("BIND property missing", e.getMessage()); + } + } + + @Test + public void missingPortThrowsConfigException() throws Exception { + File dbProps = new File(this.getClass().getResource("/brokenDbConfig2.properties").getFile()); + File canNotBeFoundFile = new File(""); + try { + new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile); + fail("PORT was not specified in properties file"); + } catch (InvalidConfigurationException e) { + assertEquals("PORT property missing", e.getMessage()); + } + } +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/MongoProcessRunnerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/MongoProcessRunnerTest.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,162 @@ +package com.redhat.thermostat.storage.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.storage.cli.internal.DBStartupConfiguration; +import com.redhat.thermostat.storage.cli.internal.MongoProcessRunner; + +public class MongoProcessRunnerTest { + + private MongoProcessRunner runner; + private DBStartupConfiguration config; + private static final String NO_JOURNAL_MONGODB_VERSION = "2.0.0"; + private static final String JOURNAL_MONGODB_VERSION = "1.8.0"; + private static final String BIND_IP = "127.0.0.1"; + private static final long PORT = 12456; + + @Before + public void setUp() { + File dbPath = new File("/path/to/db"); + File logPath = new File("/path/to/log"); + File pidFile = new File("/path/to/pid"); + config = mock(DBStartupConfiguration.class); + when(config.getBindIP()).thenReturn(BIND_IP); + when(config.getPort()).thenReturn(PORT); + when(config.getDBPath()).thenReturn(dbPath); + when(config.getLogFile()).thenReturn(logPath); + when(config.getPidFile()).thenReturn(pidFile); + runner = new MongoProcessRunner(config, false); + } + + @After + public void tearDown() { + runner = null; + config = null; + } + + @Test + public void testCommandArgumentsWithJournalVersion() throws Exception { + String[] expected = { "mongod", "--nojournal", "--quiet", "--fork", + "--auth", "--nohttpinterface", "--bind_ip", config.getBindIP(), + "--dbpath", config.getDBPath().getCanonicalPath(), "--logpath", + config.getLogFile().getCanonicalPath(), "--pidfilepath", + config.getPidFile().getCanonicalPath(), "--port", + Long.toString(config.getPort()) }; + List cmds = runner.getStartupCommand(NO_JOURNAL_MONGODB_VERSION); + String[] actual = cmds.toArray(new String[0]); + verifyEquals(expected, actual); + } + + @Test + public void testCommandArgumentsWithNoJournalVersion() throws Exception { + String[] expected = { "mongod", "--quiet", "--fork", "--auth", + "--nohttpinterface", "--bind_ip", config.getBindIP(), + "--dbpath", config.getDBPath().getCanonicalPath(), "--logpath", + config.getLogFile().getCanonicalPath(), "--pidfilepath", + config.getPidFile().getCanonicalPath(), "--port", + Long.toString(config.getPort()) }; + List cmds = runner.getStartupCommand(JOURNAL_MONGODB_VERSION); + String[] actual = cmds.toArray(new String[0]); + verifyEquals(expected, actual); + } + + @Test + public void testCommandArgumentsWithSSLEnabled() throws Exception { + when(config.isSslEnabled()).thenReturn(true); + File pemFile = new File("/path/to/cert_and_key.pem"); + when(config.getSslPemFile()).thenReturn(pemFile); + when(config.getSslKeyPassphrase()).thenReturn("non-null"); + String[] expected = { "mongod", "--quiet", "--fork", "--auth", + "--nohttpinterface", "--bind_ip", config.getBindIP(), + "--dbpath", config.getDBPath().getCanonicalPath(), "--logpath", + config.getLogFile().getCanonicalPath(), "--pidfilepath", + config.getPidFile().getCanonicalPath(), "--port", + Long.toString(config.getPort()), "--sslOnNormalPorts", + "--sslPEMKeyFile", config.getSslPemFile().getCanonicalPath(), + "--sslPEMKeyPassword", config.getSslKeyPassphrase() + }; + List cmds = runner.getStartupCommand(JOURNAL_MONGODB_VERSION); + String[] actual = cmds.toArray(new String[0]); + verifyEquals(expected, actual); + } + + @Test + public void testCommandArgumentsWithSSLEnabledThrowsInvalidConfigException() throws IOException { + when(config.isSslEnabled()).thenReturn(true); + // PEM file can't be null when SSL == true + when(config.getSslPemFile()).thenReturn(null); + try { + runner.getStartupCommand(JOURNAL_MONGODB_VERSION); + fail("Should have thrown exception!"); + } catch (InvalidConfigurationException e) { + assertEquals("No SSL PEM file specified!", e.getMessage()); + } + // Key password can't be null when SSL == true and keyfile present + File pemFile = new File("/path/to/ssl.pem"); + when(config.getSslPemFile()).thenReturn(pemFile); + when(config.getSslKeyPassphrase()).thenReturn(null); + try { + runner.getStartupCommand(JOURNAL_MONGODB_VERSION); + fail("Should have thrown exception!"); + } catch (InvalidConfigurationException e) { + assertEquals("No SSL key passphrase set!", e.getMessage()); + } + } + + @Test + public void testCheckPidNull() throws IOException { + BufferedReader reader = mock(BufferedReader.class); + when(reader.readLine()).thenReturn(null); + Integer pid = runner.doGetPid(reader); + assertNull(pid); + } + + @Test + public void canGetVersionFromVersionCmdOutputV22() throws IOException { + String versionOutput = "db version v2.2.4, pdfile version 4.5\n" + + "Mon Aug 26 17:13:45 git version: nogitversion"; + ByteArrayInputStream in = new ByteArrayInputStream(versionOutput.getBytes()); + String version = runner.doGetDBVersion(in); + assertEquals("2.2.4", version); + } + + @Test + public void canGetVersionFromVersionCmdOutputV24() throws IOException { + String versionOutput = "db version v2.4.5\n" + + "Mon Aug 26 18:01:28.404 git version: nogitversion"; + ByteArrayInputStream in = new ByteArrayInputStream(versionOutput.getBytes()); + String version = runner.doGetDBVersion(in); + assertEquals("2.4.5", version); + } + + @Test + public void canProceedIfGetDbVersionThrowsException() throws IOException { + String versionOutput = "foo\n" + + "Mon Aug 26 18:01:28.404 git version: nogitversion"; + ByteArrayInputStream in = new ByteArrayInputStream(versionOutput.getBytes()); + String version = runner.doGetDBVersion(in); + assertEquals(MongoProcessRunner.NO_JOURNAL_FIRST_VERSION, version); + } + + private void verifyEquals(String[] expected, String[] actual) { + assertEquals(expected.length, actual.length); + for (int i=0; i < expected.length; i++) { + assertEquals(expected[i], actual[i]); + } + } +} diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/StorageCommandTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/StorageCommandTest.java Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,336 @@ +/* + * Copyright 2012, 2013 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 + * . + * + * 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.storage.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +import junit.framework.Assert; + +import org.junit.After; +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.ExitStatus; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.SimpleArguments; +import com.redhat.thermostat.common.tools.ApplicationException; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.shared.config.CommonPaths; +import com.redhat.thermostat.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.shared.config.internal.CommonPathsImpl; +import com.redhat.thermostat.storage.cli.internal.DBConfig; +import com.redhat.thermostat.storage.cli.internal.DBStartupConfiguration; +import com.redhat.thermostat.storage.cli.internal.MongoProcessRunner; +import com.redhat.thermostat.storage.cli.internal.StorageCommand; +import com.redhat.thermostat.testutils.TestUtils; + +public class StorageCommandTest { + + private static final String PORT = "27518"; + private static final String BIND = "127.0.0.1"; + private static final String DB = "data/db"; + + private String tmpDir; + private ExitStatus exitStatus; + private CommonPaths paths; + + @Before + public void setup() { + exitStatus = mock(ExitStatus.class); + // need to create a dummy config file for the test + try { + Properties props = new Properties(); + + props.setProperty(DBConfig.BIND.name(), BIND); + props.setProperty(DBConfig.PORT.name(), PORT); + + tmpDir = TestUtils.setupStorageConfigs(props); + } catch (IOException e) { + Assert.fail("cannot setup tests: " + e); + } + + paths = mock(CommonPathsImpl.class); + File baseDir = new File(tmpDir); + File userRuntimeDir = new File(baseDir, "run"); + File userDataDir = new File(baseDir, "data"); + File logsDir = new File(baseDir, "logs"); + File confDir = new File(baseDir, "etc"); + + when(paths.getUserThermostatHome()).thenReturn(baseDir); + when(paths.getUserRuntimeDataDirectory()).thenReturn(userRuntimeDir); + when(paths.getUserPersistentDataDirectory()).thenReturn(userDataDir); + when(paths.getUserConfigurationDirectory()).thenReturn(confDir); + when(paths.getUserStorageDirectory()).thenCallRealMethod(); + when(paths.getUserStorageConfigurationFile()).thenCallRealMethod(); + when(paths.getUserLogDirectory()).thenReturn(logsDir); + when(paths.getUserStorageLogFile()).thenCallRealMethod(); + when(paths.getUserStoragePidFile()).thenCallRealMethod(); + + when(paths.getSystemThermostatHome()).thenReturn(baseDir); + when(paths.getSystemConfigurationDirectory()).thenCallRealMethod(); + when(paths.getSystemStorageConfigurationFile()).thenCallRealMethod(); + } + + @After + public void tearDown() { + exitStatus = null; + } + + @Test + public void testConfig() throws CommandException { + SimpleArguments args = new SimpleArguments(); + args.addArgument("quiet", null); + args.addArgument("start", null); + args.addArgument("dryRun", null); + CommandContext ctx = mock(CommandContext.class); + when(ctx.getArguments()).thenReturn(args); + + StorageCommand service = new StorageCommand(exitStatus, paths) { + @Override + MongoProcessRunner createRunner() { + throw new AssertionError("dry run should never create an actual runner"); + } + }; + + service.run(ctx); + + DBStartupConfiguration conf = service.getConfiguration(); + + Assert.assertEquals(tmpDir + DB, conf.getDBPath().getPath()); + Assert.assertEquals(Integer.parseInt(PORT), conf.getPort()); + Assert.assertEquals("mongodb://" + BIND + ":" + PORT , conf.getDBConnectionString()); + } + + private StorageCommand prepareService(boolean startSuccess) throws IOException, + InterruptedException, InvalidConfigurationException, ApplicationException + { + final MongoProcessRunner runner = mock(MongoProcessRunner.class); + if (!startSuccess) { + doThrow(new ApplicationException("mock exception")).when(runner).startService(); + } + + // TODO: stop not tested yet, but be sure it's not called from the code + doThrow(new ApplicationException("mock exception")).when(runner).stopService(); + + StorageCommand service = new StorageCommand(exitStatus, paths) { + @Override + MongoProcessRunner createRunner() { + return runner; + } + }; + + return service; + } + + @Test + public void testListeners() throws InterruptedException, IOException, ApplicationException, InvalidConfigurationException, CommandException + { + StorageCommand service = prepareService(true); + + final CountDownLatch latch = new CountDownLatch(2); + + final boolean[] result = new boolean[2]; + service.getNotifier().addActionListener(new ActionListener() { + @SuppressWarnings("incomplete-switch") + @Override + public void actionPerformed(ActionEvent actionEvent) { + switch (actionEvent.getActionId()) { + case FAIL: + result[0] = false; + latch.countDown(); + latch.countDown(); + break; + + case SUCCESS: + result[0] = true; + latch.countDown(); + break; + + case START: + result[1] = true; + latch.countDown(); + break; + } + } + }); + + service.run(prepareContext()); + latch.await(); + + Assert.assertTrue(result[0]); + Assert.assertTrue(result[1]); + } + + @Test + public void testListenersFail() throws InterruptedException, IOException, ApplicationException, CommandException, InvalidConfigurationException + { + StorageCommand service = prepareService(false); + + final CountDownLatch latch = new CountDownLatch(1); + final boolean[] result = new boolean[1]; + service.getNotifier().addActionListener(new ActionListener() { + @SuppressWarnings("incomplete-switch") + @Override + public void actionPerformed(ActionEvent actionEvent) { + switch (actionEvent.getActionId()) { + case FAIL: + result[0] = true; + break; + + case SUCCESS: + result[0] = false; + break; + } + latch.countDown(); + } + }); + + service.run(prepareContext()); + latch.await(); + + Assert.assertTrue(result[0]); + } + + @Test + public void exceptionSetsExitStatusOnFailure() throws Exception { + this.exitStatus = new ExitStatus() { + + private int exitStatus = -1; + + @Override + public void setExitStatus(int newExitStatus) { + exitStatus = newExitStatus; + } + + @Override + public int getExitStatus() { + return exitStatus; + } + }; + assertEquals(-1, this.exitStatus.getExitStatus()); + StorageCommand command = prepareService(false); + final CountDownLatch latch = new CountDownLatch(1); + final boolean[] result = new boolean[1]; + command.getNotifier().addActionListener(new ActionListener() { + @SuppressWarnings("incomplete-switch") + @Override + public void actionPerformed(ActionEvent actionEvent) { + switch (actionEvent.getActionId()) { + case FAIL: + result[0] = true; + break; + + case SUCCESS: + result[0] = false; + break; + } + latch.countDown(); + } + }); + command.run(prepareContext()); + latch.await(); + // should have failed + assertTrue(result[0]); + assertEquals(ExitStatus.EXIT_ERROR, this.exitStatus.getExitStatus()); + } + + @Test + public void exitStatusRemainsUntouchedOnSuccess() throws Exception { + this.exitStatus = new ExitStatus() { + + private int exitStatus = -1; + + @Override + public void setExitStatus(int newExitStatus) { + exitStatus = newExitStatus; + } + + @Override + public int getExitStatus() { + return exitStatus; + } + }; + StorageCommand command = prepareService(true); + final CountDownLatch latch = new CountDownLatch(1); + final boolean[] result = new boolean[1]; + command.getNotifier().addActionListener(new ActionListener() { + @SuppressWarnings("incomplete-switch") + @Override + public void actionPerformed(ActionEvent actionEvent) { + switch (actionEvent.getActionId()) { + case FAIL: + result[0] = false; + break; + + case SUCCESS: + result[0] = true; + break; + } + latch.countDown(); + } + }); + command.run(prepareContext()); + latch.await(); + // should have worked + assertTrue(result[0]); + // this impl of ExitStatus has a default value of -1 + assertEquals(-1, this.exitStatus.getExitStatus()); + } + + private CommandContext prepareContext() { + SimpleArguments args = new SimpleArguments(); + args.addArgument("quiet", "--quiet"); + args.addArgument("start", "--start"); + CommandContext ctx = mock(CommandContext.class); + when(ctx.getArguments()).thenReturn(args); + return ctx; + } + +} + diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/test/resources/brokenDbConfig.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/resources/brokenDbConfig.properties Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,2 @@ +PORT=27518 +# missing BIND \ No newline at end of file diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/test/resources/brokenDbConfig2.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/resources/brokenDbConfig2.properties Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,2 @@ +#PORT=27518 +BIND=127.0.0.1 \ No newline at end of file diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/test/resources/testDbConfig.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/resources/testDbConfig.properties Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,7 @@ +PORT=27518 +BIND=127.0.0.1 + +## SSL config fluff +SSL_ENABLE=true +SSL_PEM_FILE=/path/to/some/pem/file.pem +SSL_KEY_PASSWORD=somepassword \ No newline at end of file diff -r 869c5e163e7a -r 7caa7f9afb14 storage/cli/src/test/resources/testDbConfig2.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/resources/testDbConfig2.properties Mon Feb 10 19:08:50 2014 -0500 @@ -0,0 +1,7 @@ +PORT=27518 +BIND=127.0.0.1 + +## SSL config fluff +#SSL_ENABLE=true +#SSL_PEM_FILE=/path/to/some/pem/file.pem +#SSL_KEY_PASSWORD=somepassword \ No newline at end of file diff -r 869c5e163e7a -r 7caa7f9afb14 storage/pom.xml --- a/storage/pom.xml Fri Dec 20 11:42:37 2013 +0100 +++ b/storage/pom.xml Mon Feb 10 19:08:50 2014 -0500 @@ -60,6 +60,7 @@ core + cli mongo