changeset 66:1fdd16a78761

client starts own agent
author Jon VanAlten <jon.vanalten@redhat.com>
date Thu, 02 Feb 2012 10:53:45 -0500
parents ad21cb32aad1
children 3b1baa5f0bf2
files build.xml config/agent.properties scripts/thermostat-agent scripts/thermostat-client-gui src/com/redhat/thermostat/client/ClientArgs.java src/com/redhat/thermostat/client/Main.java src/com/redhat/thermostat/client/MongoConnection.java src/com/redhat/thermostat/common/Constants.java
diffstat 8 files changed, 185 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/build.xml	Wed Feb 01 17:00:45 2012 -0500
+++ b/build.xml	Thu Feb 02 10:53:45 2012 -0500
@@ -100,7 +100,7 @@
     <target name="build-client-gui-classes" depends="build-common-classes">
         <mkdir dir="${classes.dir}"/>
         <javac srcdir="${src.dir}" destdir="${classes.dir}" debug="true">
-            <include name="${client.path.prefix}**" />
+            <include name="${client-gui.path.prefix}**" />
             <classpath>
                 <path location="${classes.dir}" />
                 <path refid="client-gui.classpath" />
@@ -112,8 +112,8 @@
         <mkdir dir="${dist.dir}" />
         <jar destfile="${client-gui.jar}" duplicate="fail">
             <fileset dir="${classes.dir}">
-                <include name="${client.path.prefix}**/*.class" />
-                <include name="${client.path.prefix}**/*.properties" />
+                <include name="${client-gui.path.prefix}**/*.class" />
+                <include name="${client-gui.path.prefix}**/*.properties" />
             </fileset>
          </jar>
     </target>
--- a/config/agent.properties	Wed Feb 01 17:00:45 2012 -0500
+++ b/config/agent.properties	Thu Feb 02 10:53:45 2012 -0500
@@ -4,9 +4,13 @@
 mongo_launch_script=@THERM_INSTALL_DIR@/scripts/localmongo.sh
 # To be used when connecting on localhost without --local argument (cluster)
 mongos_port=27517
-### Below settings are used only by the mongo launch script
+### Next few settings are used only by the mongo launch script
 config_port=27519
 config_url=mongodb://127.0.0.1
+### End section specific to launch script
+### Properties needed by client
+agent_launch_script=@THERM_INSTALL_DIR@/scripts/thermostat-agent
+### End properties needed by client
 #
 ## Backend Configuration
 # This must be a comma separated list naming the fully qualified class name for
--- a/scripts/thermostat-agent	Wed Feb 01 17:00:45 2012 -0500
+++ b/scripts/thermostat-agent	Thu Feb 02 10:53:45 2012 -0500
@@ -38,7 +38,7 @@
 #
 # Some necessary variables.
 JAVA_DIR="@JAVA_DIR@"
-JAVA_EXE="@JAVA_HOME@/bin/java"
+JAVA="@JAVA_HOME@/bin/java"
 JCOMMON_JAR="${JAVA_DIR}/jcommon.jar"
 MONGO_JAR="${JAVA_DIR}/mongo.jar"
 BSON_JAR="${JAVA_DIR}/bson.jar"
@@ -62,7 +62,7 @@
 }
 
 function run_agent {
-  ${JAVA_EXE} -cp ${AGENT_CLASSPATH} ${AGENT_MAIN} ${AGENT_ARGS}
+  ${JAVA} -cp ${AGENT_CLASSPATH} ${AGENT_MAIN} ${AGENT_ARGS}
 }
 
 function run_local {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/thermostat-client-gui	Thu Feb 02 10:53:45 2012 -0500
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# Copyright 2012 Red Hat, Inc.
+#
+# This file is part of Thermostat.
+#
+# Thermostat is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published
+# by the Free Software Foundation; either version 2, or (at your
+# option) any later version.
+#
+# Thermostat is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Thermostat; see the file COPYING.  If not see
+# <http://www.gnu.org/licenses/>.
+#
+# Linking this code with other modules is making a combined work
+# based on this code.  Thus, the terms and conditions of the GNU
+# General Public License cover the whole combination.
+#
+# As a special exception, the copyright holders of this code give
+# you permission to link this code with independent modules to
+# produce an executable, regardless of the license terms of these
+# independent modules, and to copy and distribute the resulting
+# executable under terms of your choice, provided that you also
+# meet, for each linked independent module, the terms and conditions
+# of the license of that module.  An independent module is a module
+# which is not derived from or based on this code.  If you modify
+# this code, you may extend this exception to your version of the
+# library, but you are not obligated to do so.  If you do not wish
+# to do so, delete this exception statement from your version.
+#
+#####################################################################
+#
+# Some necessary variables.
+JAVA_DIR="@JAVA_DIR@"
+JAVA="@JAVA_HOME@/bin/java"
+JCOMMON_JAR="${JAVA_DIR}/jcommon.jar"
+JFREECHART_JAR="${JAVA_DIR}/jfreechart/jfreechart.jar"
+MONGO_JAR="${JAVA_DIR}/mongo.jar"
+BSON_JAR="${JAVA_DIR}/bson.jar"
+TOOLS_JAR="@JAVA_HOME@/../lib/tools.jar"
+CLIENT_CLASSPATH="${JCOMMON_JAR}:${JFREECHART_JAR}:${MONGO_JAR}:${BSON_JAR}:${TOOLS_JAR}"
+# Find the directory where thermostat is installed.
+# Note this will not work if there are symlinks to resolve that
+# are not full paths.
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
+THERM_DIR=`dirname "$( cd -P "$( dirname "$SOURCE" )" && pwd )"`
+# Some other necessary variables.
+CLIENT_CLASSPATH="${CLIENT_CLASSPATH}:${THERM_DIR}/lib/thermostat-client-gui.jar:${THERM_DIR}/lib/thermostat-common.jar"
+CLIENT_MAIN="com.redhat.thermostat.client.Main"
+
+CLIENT_ARGS="--properties ${THERM_DIR}/config/agent.properties"
+
+function usage {
+  echo "Usage:"
+  echo "  thermostat-client-gui [args]"
+}
+
+${JAVA} -cp ${CLIENT_CLASSPATH} ${CLIENT_MAIN} ${CLIENT_ARGS} &
+
--- a/src/com/redhat/thermostat/client/ClientArgs.java	Wed Feb 01 17:00:45 2012 -0500
+++ b/src/com/redhat/thermostat/client/ClientArgs.java	Thu Feb 02 10:53:45 2012 -0500
@@ -37,28 +37,43 @@
 package com.redhat.thermostat.client;
 
 import java.awt.Window;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Properties;
 
 import com.redhat.thermostat.client.ui.LayoutDebugHelper;
 
 public class ClientArgs {
 
-    private static boolean isDebugLayout =
-            Boolean.getBoolean("thermostat.debug-layout");
-
-    // private static boolean isDebugLayout = true;
-
-    private boolean dummyDataSource = false;
+    private boolean isDebugLayout = Boolean.getBoolean("thermostat.debug-layout");
+    private Properties props = new Properties();
 
-    public ClientArgs(String[] initialArgs) {
-        // remove 'unused' warnings
-        for (String arg : initialArgs) {
-            if (arg.equals("--debug-layout")) {
+    public ClientArgs(String[] args) {
+        boolean hasValidPropertiesArg = false;
+        for (int i=0; i < args.length; i++) {
+            if (args[i].equals("--debug-layout")) {
                 isDebugLayout = true;
-            } else if (arg.equals("--dummy-data-source")) {
-                dummyDataSource = true;
+            } else if (args[i].equals("--properties")) {
+                i++;
+                if (i >= args.length) {
+                    throw new IllegalArgumentException("--properties argument requires filename.");
+                }
+                try {
+                    props.load(new FileReader(args[i]));
+                } catch (FileNotFoundException e) {
+                    // ignore
+                } catch (IOException e) {
+                    // ignore
+                }
+                // Ok props file must be good.
+                hasValidPropertiesArg = true;
             }
         }
-        // TODO what arguments do we care about?
+        if (!hasValidPropertiesArg) {
+            throw new IllegalArgumentException("Properties argument with valid properties file is required.");
+        }
+        // TODO what other arguments do we care about?
         // perhaps skipping the mode selection?
 
         if (isDebugLayout()) {
@@ -85,11 +100,11 @@
         }
     }
 
-    public static boolean isDebugLayout() {
+    public boolean isDebugLayout() {
         return isDebugLayout;
     }
 
-    public boolean useDummyDataSource() {
-        return dummyDataSource;
+    public Properties getProperties() {
+        return props;
     }
 }
--- a/src/com/redhat/thermostat/client/Main.java	Wed Feb 01 17:00:45 2012 -0500
+++ b/src/com/redhat/thermostat/client/Main.java	Thu Feb 02 10:53:45 2012 -0500
@@ -61,24 +61,14 @@
     private UiFacadeFactory uiFacadeFactory;
 
     private Main(String[] args) {
-        this.arguments = new ClientArgs(args);
+        try {
+            this.arguments = new ClientArgs(args);
+        } catch (IllegalArgumentException ex) {
+            logger.log(Level.SEVERE, "Bad arguments to Thermostat client.", ex);
+            System.exit(-1);
+        }
 
-        if (arguments.useDummyDataSource()) {
-            logger.log(Level.CONFIG, "using dummy data");
-            connection = new Connection() {
-                @Override
-                public void disconnect() {
-                    /* no op */
-                }
-
-                @Override
-                public void connect() {
-                    /* no op */
-                }
-            };
-        } else {
-            connection = new MongoConnection();
-        }
+        connection = new MongoConnection(arguments.getProperties());
     }
 
     private void showGui() {
--- a/src/com/redhat/thermostat/client/MongoConnection.java	Wed Feb 01 17:00:45 2012 -0500
+++ b/src/com/redhat/thermostat/client/MongoConnection.java	Thu Feb 02 10:53:45 2012 -0500
@@ -36,7 +36,10 @@
 
 package com.redhat.thermostat.client;
 
+import java.io.IOException;
+import java.io.OutputStream;
 import java.net.UnknownHostException;
+import java.util.Properties;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -45,6 +48,8 @@
 import com.mongodb.MongoException;
 import com.mongodb.MongoURI;
 import com.redhat.thermostat.agent.storage.StorageConstants;
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.NotImplementedException;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
 public class MongoConnection extends Connection {
@@ -52,34 +57,33 @@
 
     private Mongo m = null;
     private DB db = null;
-    private boolean isLocal = false;
+    private boolean hasLocalAgent = false;
+    private Process localAgentProcess = null;
+    private Properties props;
+
+    public MongoConnection(Properties props) {
+        this.props = props;
+    }
 
     @Override
     public void connect() {
-        switch (getType()) {
-            case LOCAL:
-                try {
-                    m = new Mongo(getMongoURI());
-                    db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME);
-                    /* the mongo java driver does not ensure this connection is actually working */
-                    testConnection(db);
-                } catch (MongoException e) {
-                    fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-                    return;
-                } catch (UnknownHostException e) {
-                    fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-                    return;
-                }
-                isLocal = true;
-                break;
-            case REMOTE:
-                // TODO connect to a remote machine
-                fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-                return;
-            case CLUSTER:
-                // TODO connect to a cluster
-                fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-                return;
+        try {
+            m = new Mongo(getMongoURI());
+            db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME);
+            /* the mongo java driver does not ensure this connection is actually working */
+            testConnection(db);
+        } catch (MongoException e) {
+            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
+            return;
+        } catch (UnknownHostException e) {
+            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
+            return;
+        } catch (LocalAgentException e) {
+            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
+            return;
+        } catch (NotImplementedException e) {
+            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
+            return;
         }
         fireChanged(ConnectionStatus.CONNECTED);
         connected = true;
@@ -90,24 +94,39 @@
     }
 
     private MongoURI getMongoURI() {
-        MongoURI uri = new MongoURI("mongodb://127.0.0.1:27017");
+        MongoURI uri = null;
         switch (getType()) {
             case LOCAL:
                 startLocalAgent();
-                // FIXME hardcorded address, use config/agent.properties instead.
-                uri = new MongoURI("mongodb://127.0.0.1:27518");
+                uri = new MongoURI("mongodb://127.0.0.1:"
+                        + props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT));
             case REMOTE:
-                // TODO
+                throw new NotImplementedException("No mongo URI implemented for REMOTE.");
             case CLUSTER:
-                // TODO
+                throw new NotImplementedException("No mongo URI implemented for CLUSTER.");
         }
         return uri;
     }
 
-    private void startLocalAgent() {
-        // TODO implement this
-        logger.log(Level.WARNING, "startLocalAgent not implemented");
-        logger.log(Level.WARNING, "please start mongodb and agent yourself");
+    private void startLocalAgent() throws LocalAgentException {
+        int status = 0;
+        try {
+            String agentCommand = props.getProperty(Constants.CLIENT_PROPERTY_AGENT_LAUNCH_SCRIPT) + " --local";
+            localAgentProcess = Runtime.getRuntime().exec(agentCommand);
+            // Allow some time for things to get started.
+            try {
+                // TODO provide some UI feedback here instead of just seeming dead.
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        } catch (IOException e) {
+            throw new LocalAgentException();
+        }
+        if (status != 0) {
+            throw new LocalAgentException();
+        }
+        hasLocalAgent = true;
     }
 
     public DB getDB() {
@@ -119,16 +138,28 @@
         if (m != null) {
             m.close();
         }
-        if (isLocal) {
+        if (hasLocalAgent) {
             stopLocalAgent();
         }
         connected = false;
     }
 
     private void stopLocalAgent() {
-        // TODO implement this
-        logger.log(Level.WARNING, "stopLocalAgent not implemented");
-        logger.log(Level.WARNING, "please stop mongodb and agent yourself");
+        // TODO this is currently using Agent's 'run until some data avail on stdin' hack.
+        // That hack will go away, at which point we will need another way to shut down.
+        OutputStream agentIn = localAgentProcess.getOutputStream();
+        byte[] anything = { 0x04 };
+        try {
+            agentIn.write(anything);
+            agentIn.flush();
+            localAgentProcess.waitFor();
+        } catch (IOException e) {
+            logger.warning("Error shutting down local agent.");
+        } catch (InterruptedException e) {
+            logger.warning("Interrupted waiting for local agent to shut down.");
+        }
     }
 
+    private class LocalAgentException extends RuntimeException {
+    }
 }
--- a/src/com/redhat/thermostat/common/Constants.java	Wed Feb 01 17:00:45 2012 -0500
+++ b/src/com/redhat/thermostat/common/Constants.java	Thu Feb 02 10:53:45 2012 -0500
@@ -62,6 +62,8 @@
     public static final String AGENT_PROPERTY_BACKENDS = "backends";
     public static final String AGENT_PROPERTY_BACKEND_ACTIVE = "active";
 
+    public static final String CLIENT_PROPERTY_AGENT_LAUNCH_SCRIPT = "agent_launch_script";
+
     public static final String AGENT_LOCAL_HOSTNAME = "localhost";
 
     public static final long KILOBYTES_TO_BYTES = 1000;