changeset 1523:df5a0ae22f11

List Agents command addition. Reviewed-by: omajid, neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-October/011205.html
author Jie Kang <jkang@redhat.com>
date Thu, 16 Oct 2014 09:41:45 -0400
parents a036c40713da
children 280587a7c6c2
files client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/AgentListFormatter.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ListAgentsCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ListVMsCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ActivatorTest.java client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/AgentListFormatterTest.java client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ListAgentsCommandTest.java common/core/src/main/java/com/redhat/thermostat/common/cli/AbstractCommand.java distribution/config/commands/list-agents.properties killvm/command/pom.xml killvm/command/src/main/java/com/redhat/thermostat/killvm/command/KillVMCommand.java
diffstat 14 files changed, 523 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java	Wed Oct 15 14:46:57 2014 -0400
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java	Thu Oct 16 09:41:45 2014 -0400
@@ -47,6 +47,7 @@
 import com.redhat.thermostat.common.cli.CommandRegistryImpl;
 import com.redhat.thermostat.common.config.ClientPreferences;
 import com.redhat.thermostat.shared.config.CommonPaths;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
 import com.redhat.thermostat.utils.keyring.Keyring;
 
 public class Activator implements BundleActivator {
@@ -54,6 +55,9 @@
     private CommandRegistry reg = null;
     private MultipleServiceTracker tracker;
 
+    private MultipleServiceTracker agentTracker;
+    private final ListAgentsCommand listAgentsCommand = new ListAgentsCommand();
+
     @Override
     public void start(final BundleContext context) throws Exception {
         reg = new CommandRegistryImpl(context);
@@ -86,11 +90,31 @@
             
         });
         tracker.open();
+
+        Class<?>[] agentClasses = new Class[] {
+                AgentInfoDAO.class,
+        };
+        agentTracker = new MultipleServiceTracker(context, agentClasses, new Action() {
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                AgentInfoDAO agentInfoDAO = (AgentInfoDAO) services.get(AgentInfoDAO.class.getName());
+                listAgentsCommand.setAgentInfoDAO(agentInfoDAO);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                listAgentsCommand.setAgentInfoDAO(null);
+            }
+        });
+        agentTracker.open();
+
+        reg.registerCommand("list-agents", listAgentsCommand);
     }
 
     @Override
     public void stop(BundleContext context) throws Exception {
         tracker.close();
+        agentTracker.close();
         reg.unregisterCommands();
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/AgentListFormatter.java	Thu Oct 16 09:41:45 2014 -0400
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.cli.internal;
+
+import java.io.PrintStream;
+import java.text.DateFormat;
+import java.util.Date;
+
+import com.redhat.thermostat.common.cli.TableRenderer;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.model.AgentInformation;
+
+
+public class AgentListFormatter{
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+    private static final DateFormat dateFormat = DateFormat.getDateTimeInstance();
+    private static final int NUM_COLUMNS = 4;
+
+    private static final String AGENT_ID = translator.localize(LocaleResources.AGENT_ID).getContents();
+    private static final String CONFIG_LISTEN_ADDRESS = translator.localize(LocaleResources.CONFIG_LISTEN_ADDRESS).getContents();
+    private static final String START_TIME = translator.localize(LocaleResources.START_TIME).getContents();
+    private static final String STOP_TIME = translator.localize(LocaleResources.STOP_TIME).getContents();
+
+    private final TableRenderer tableRenderer = new TableRenderer(NUM_COLUMNS);
+
+    void addHeader() {
+        printLine(AGENT_ID, CONFIG_LISTEN_ADDRESS, START_TIME, STOP_TIME);
+    }
+
+    void addAgent(AgentInformation info) {
+        printAgent(info);
+    }
+
+    void format(PrintStream out) {
+        tableRenderer.render(out);
+    }
+
+    private void printAgent(AgentInformation info) {
+        String startTime = dateFormat.format(new Date(info.getStartTime()));
+        String stopTime = getStopTimeMessage(info.getStopTime());
+
+        printLine(info.getAgentId(),
+                info.getConfigListenAddress(),
+                startTime,
+                stopTime);
+    }
+
+    private String getStopTimeMessage(long stopTime) {
+        if (stopTime == 0) {
+            return translator.localize(LocaleResources.AGENT_ACTIVE).getContents();
+        } else {
+            return dateFormat.format(new Date(stopTime));
+        }
+    }
+
+    private void printLine(String agentId, String address, String startTime, String stopTime) {
+        tableRenderer.printLine(agentId, address, startTime, stopTime);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ListAgentsCommand.java	Thu Oct 16 09:41:45 2014 -0400
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.cli.internal;
+
+import java.io.PrintStream;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import com.redhat.thermostat.common.cli.AbstractCommand;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+
+public class ListAgentsCommand extends AbstractCommand {
+
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private AgentInfoDAO agentInfoDAO;
+    private Semaphore servicesAvailable = new Semaphore(0);
+
+
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        waitForServices(500l);
+
+        requireNotNull(agentInfoDAO, translator.localize(LocaleResources.AGENT_SERVICE_UNAVAILABLE));
+
+        listAgents(ctx.getConsole().getOutput(), agentInfoDAO.getAllAgentInformation());
+    }
+
+    private void waitForServices(long timeout) {
+        try {
+            servicesAvailable.tryAcquire(timeout, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            translator.localize(LocaleResources.COMMAND_INTERRUPTED);
+        }
+    }
+
+    private void listAgents(PrintStream out, List<AgentInformation> informationList) {
+        AgentListFormatter formatter = new AgentListFormatter();
+        formatter.addHeader();
+
+        for (AgentInformation info : informationList) {
+            formatter.addAgent(info);
+        }
+
+        formatter.format(out);
+    }
+
+    public void setAgentInfoDAO(AgentInfoDAO agentInfoDAO) {
+        this.agentInfoDAO = agentInfoDAO;
+        if (agentInfoDAO == null) {
+            servicesUnavailable();
+        } else {
+            servicesAvailable();
+        }
+
+
+    }
+
+    private void servicesAvailable() {
+        this.servicesAvailable.release();
+    }
+
+    private void servicesUnavailable() {
+        this.servicesAvailable.drainPermits();
+    }
+}
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ListVMsCommand.java	Wed Oct 15 14:46:57 2014 -0400
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ListVMsCommand.java	Thu Oct 16 09:41:45 2014 -0400
@@ -83,6 +83,7 @@
         }
         VmInfoDAO vmsDAO = (VmInfoDAO) context.getService(vmsDAORef);
         VMListFormatter formatter = new VMListFormatter();
+        formatter.addHeader();
         for (HostRef host : hosts) {
             Collection<VmRef> vms = vmsDAO.getVMs(host);
             for (VmRef vm : vms) {
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Wed Oct 15 14:46:57 2014 -0400
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Thu Oct 16 09:41:45 2014 -0400
@@ -47,6 +47,7 @@
     VM_SERVICE_UNAVAILABLE,
     VM_CPU_SERVICE_NOT_AVAILABLE,
     VM_MEMORY_SERVICE_NOT_AVAILABLE,
+    AGENT_SERVICE_UNAVAILABLE,
 
     COMMAND_CONNECT_ALREADY_CONNECTED,
     COMMAND_CONNECT_FAILED_TO_CONNECT,
@@ -60,6 +61,8 @@
 
     COMMAND_SHELL_IO_EXCEPTION,
 
+    COMMAND_INTERRUPTED,
+
     VM_INFO_VM_ID,
     VM_INFO_PROCESS_ID,
     VM_INFO_START_TIME,
@@ -72,6 +75,12 @@
     VM_INFO_USER,
     VM_INFO_USER_UNKNOWN,
 
+    AGENT_ID,
+    CONFIG_LISTEN_ADDRESS,
+    START_TIME,
+    STOP_TIME,
+    AGENT_ACTIVE,
+
     COLUMN_HEADER_HOST_ID,
     COLUMN_HEADER_HOST,
     COLUMN_HEADER_VM_ID,
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java	Wed Oct 15 14:46:57 2014 -0400
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java	Thu Oct 16 09:41:45 2014 -0400
@@ -46,6 +46,7 @@
 class VMListFormatter {
 
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+    private static final int NUM_COLUMNS = 6;
 
     private static final String HOST_ID = translator.localize(LocaleResources.COLUMN_HEADER_HOST_ID).getContents();
     private static final String HOST = translator.localize(LocaleResources.COLUMN_HEADER_HOST).getContents();
@@ -57,23 +58,18 @@
     private static final String STATUS_ALIVE = translator.localize(LocaleResources.VM_STATUS_ALIVE).getContents();
     private static final String STATUS_DEAD = translator.localize(LocaleResources.VM_STATUS_DEAD).getContents();
 
-    private TableRenderer tableRenderer;
-
-    VMListFormatter() {
-        tableRenderer = new TableRenderer(6);
-        printHeader();
-    }
+    private final TableRenderer tableRenderer = new TableRenderer(NUM_COLUMNS);
 
     void addVM(VmRef vm, VmInfo info) {
         printVM(vm, info);
     }
 
-    void format(PrintStream output) {
-        tableRenderer.render(output);
+    void addHeader() {
+        printLine(HOST_ID, HOST, VM_ID, VM_PID, VM_STATUS, VM_NAME);
     }
 
-    private void printHeader() {
-        printLine(HOST_ID, HOST, VM_ID, VM_PID, VM_STATUS, VM_NAME);
+    void format(PrintStream out) {
+        tableRenderer.render(out);
     }
 
     private void printVM(VmRef vm, VmInfo info) {
--- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Wed Oct 15 14:46:57 2014 -0400
+++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Thu Oct 16 09:41:45 2014 -0400
@@ -5,6 +5,7 @@
 VM_SERVICE_UNAVAILABLE = Unable to get vm information (VmInfoDAO is unavailable)
 VM_CPU_SERVICE_NOT_AVAILABLE = Unable to access vm cpu information (VmCpuStats not available)
 VM_MEMORY_SERVICE_NOT_AVAILABLE = Unable to access vm memory information (VmCpuStats not available)
+AGENT_SERVICE_UNAVAILABLE = Agent Info Service is Unavailable (AgentInfoDAO)
 
 COMMAND_CONNECT_ALREADY_CONNECTED = Already connected to storage: URL = {0}\nPlease use disconnect command to disconnect.
 COMMAND_CONNECT_FAILED_TO_CONNECT = Could not connect to db {0}
@@ -18,6 +19,8 @@
 
 COMMAND_SHELL_IO_EXCEPTION = IOException caught during Thermostat shell session.
 
+COMMAND_INTERRUPTED = ListAgents Command interrupted while waiting for services
+
 VM_INFO_VM_ID = VM ID:
 VM_INFO_PROCESS_ID = Process ID:
 VM_INFO_START_TIME = Start time:
@@ -30,6 +33,13 @@
 VM_INFO_USER = User ID:
 VM_INFO_USER_UNKNOWN = <Unknown>
 
+AGENT_ID = Agent ID
+CONFIG_LISTEN_ADDRESS = Command Channel Address
+START_TIME = Start Time
+STOP_TIME = Stop Time
+AGENT_ACTIVE = Currently Active
+
+
 COLUMN_HEADER_HOST_ID = HOST_ID
 COLUMN_HEADER_HOST = HOST
 COLUMN_HEADER_VM_ID  = VM_ID
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ActivatorTest.java	Wed Oct 15 14:46:57 2014 -0400
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ActivatorTest.java	Thu Oct 16 09:41:45 2014 -0400
@@ -93,6 +93,7 @@
         assertCommandIsRegistered(ctx, "shell", ShellCommand.class);
         assertCommandIsRegistered(ctx, "vm-info", VMInfoCommand.class);
         assertCommandIsRegistered(ctx, "vm-stat", VMStatCommand.class);
+        assertCommandIsRegistered(ctx, "list-agents", ListAgentsCommand.class);
 
         activator.stop(ctx);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/AgentListFormatterTest.java	Thu Oct 16 09:41:45 2014 -0400
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.cli.internal;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.text.DateFormat;
+import java.util.Date;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.model.AgentInformation;
+
+public class AgentListFormatterTest {
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private final String AGENT_ID = translator.localize(LocaleResources.AGENT_ID).getContents();
+    private final String CONFIG_LISTEN_ADDRESS = translator.localize(LocaleResources.CONFIG_LISTEN_ADDRESS).getContents();
+    private final String START_TIME = translator.localize(LocaleResources.START_TIME).getContents();
+    private final String STOP_TIME = translator.localize(LocaleResources.STOP_TIME).getContents();
+
+    private final AgentListFormatter formatter = new AgentListFormatter();
+
+    private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    private final PrintStream out = new PrintStream(baos);
+
+    @Test
+    public void testPrintHeader() {
+        formatter.addHeader();
+        formatter.format(out);
+
+        String output = new String(baos.toByteArray());
+        assertTrue(output.contains(AGENT_ID));
+        assertTrue(output.contains(CONFIG_LISTEN_ADDRESS));
+        assertTrue(output.contains(START_TIME));
+        assertTrue(output.contains(STOP_TIME));
+    }
+
+    @Test
+    public void testPrintAgentInfo() {
+        String agentId = "liveAgent";
+        String address = "configListenAddress";
+        long startTime = 0;
+        long stopTime = 1;
+
+        AgentInformation info = new AgentInformation();
+        info.setAgentId(agentId);
+        info.setConfigListenAddress(address);
+        info.setStartTime(startTime);
+        info.setStopTime(stopTime);
+
+        formatter.addAgent(info);
+        formatter.format(out);
+
+        String output = new String(baos.toByteArray());
+
+        DateFormat dateFormat = DateFormat.getDateTimeInstance();
+
+        assertTrue(output.contains(agentId));
+        assertTrue(output.contains(address));
+        assertTrue(output.contains(dateFormat.format(new Date(startTime))));
+        assertTrue(output.contains(dateFormat.format(new Date(stopTime))));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ListAgentsCommandTest.java	Thu Oct 16 09:41:45 2014 -0400
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.cli.internal;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.text.DateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+import org.junit.Before;
+import org.junit.Test;
+
+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.shared.locale.Translate;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+import com.redhat.thermostat.test.TestCommandContextFactory;
+
+public class ListAgentsCommandTest {
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private ListAgentsCommand command;
+
+    private TestCommandContextFactory cmdCtxFactory;
+    private AgentInfoDAO agentInfoDAO;
+
+    private DateFormat dateFormat;
+
+
+    @Before
+    public void setup() {
+        dateFormat = DateFormat.getDateTimeInstance();
+
+        cmdCtxFactory = new TestCommandContextFactory();
+        agentInfoDAO = mock(AgentInfoDAO.class);
+
+        command = new ListAgentsCommand();
+
+    }
+
+    @Test
+    public void testListAgents() throws CommandException {
+        String idOne = "agentOne";
+        String address = "configListenAddress";
+        long startTime = 0;
+        long stopTime = 1;
+
+        AgentInformation agentOne = new AgentInformation();
+        agentOne.setAgentId(idOne);
+        agentOne.setConfigListenAddress(address);
+        agentOne.setStartTime(startTime);
+        agentOne.setStopTime(stopTime);
+
+
+        String idTwo = "agentTwo";
+        AgentInformation agentTwo = new AgentInformation();
+        agentTwo.setAgentId(idTwo);
+        agentTwo.setConfigListenAddress(address);
+        agentTwo.setStartTime(startTime);
+        agentTwo.setStopTime(stopTime);
+
+        when(agentInfoDAO.getAllAgentInformation()).thenReturn(Arrays.asList(new AgentInformation[]{agentOne, agentTwo}));
+
+        command.setAgentInfoDAO(agentInfoDAO);
+
+        CommandContext context = cmdCtxFactory.createContext(new SimpleArguments());
+
+        command.run(context);
+
+        String output = cmdCtxFactory.getOutput();
+
+        verifyHeader(output);
+        verifyAgentPrinted(output, idOne, address, startTime, stopTime);
+        verifyAgentPrinted(output, idTwo, address, startTime, stopTime);
+    }
+
+    private void verifyAgentPrinted(String output, String agentId, String address, long startTime, long stopTime) {
+        assertTrue(output.contains(agentId));
+        assertTrue(output.contains(address));
+        assertTrue(output.contains(dateFormat.format(new Date(startTime))));
+        assertTrue(output.contains(dateFormat.format(new Date(stopTime))));
+    }
+
+    private void verifyHeader(String output) {
+        String AGENT_ID = translator.localize(LocaleResources.AGENT_ID).getContents();
+        String CONFIG_LISTEN_ADDRESS = translator.localize(LocaleResources.CONFIG_LISTEN_ADDRESS).getContents();
+        String START_TIME = translator.localize(LocaleResources.START_TIME).getContents();
+        String STOP_TIME = translator.localize(LocaleResources.STOP_TIME).getContents();
+
+
+        assertTrue(output.contains(AGENT_ID));
+        assertTrue(output.contains(CONFIG_LISTEN_ADDRESS));
+        assertTrue(output.contains(START_TIME));
+        assertTrue(output.contains(STOP_TIME));
+    }
+
+    @Test(expected = CommandException.class)
+    public void testListAgentsWithoutServices() throws CommandException {
+        CommandContext context = cmdCtxFactory.createContext(new SimpleArguments());
+
+        command.run(context);
+    }
+}
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/AbstractCommand.java	Wed Oct 15 14:46:57 2014 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/AbstractCommand.java	Thu Oct 16 09:41:45 2014 -0400
@@ -36,6 +36,8 @@
 
 package com.redhat.thermostat.common.cli;
 
+import com.redhat.thermostat.shared.locale.LocalizedString;
+
 /**
  * A partial implementation of {@link Command} that most implementations should
  * extend. By default, any extension of this class will require {@link Storage},
@@ -52,5 +54,11 @@
         return true;
     }
 
+    public void requireNotNull(Object obj, LocalizedString message) throws CommandException {
+        if (obj == null) {
+            throw new CommandException(message);
+        }
+    }
+
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/config/commands/list-agents.properties	Thu Oct 16 09:41:45 2014 -0400
@@ -0,0 +1,22 @@
+bundles = com.redhat.thermostat.client.cli=${project.version}, \
+          com.redhat.thermostat.storage.mongodb=${project.version}, \
+          com.redhat.thermostat.web.common=${project.version}, \
+          com.redhat.thermostat.web.client=${project.version}, \
+          org.apache.httpcomponents.httpcore=${httpcomponents.core.version}, \
+          org.apache.httpcomponents.httpclient=${httpcomponents.client.version}, \
+          ${osgi.compendium.bundle.symbolic-name}=${osgi.compendium.osgi-version}, \
+          com.google.gson=${gson.version}, \
+          org.mongodb.mongo-java-driver=${mongo-driver.osgi-version}, \
+          org.apache.commons.beanutils=${commons-beanutils.version}, \
+          org.apache.commons.codec=${commons-codec.osgi-version}, \
+          org.apache.commons.collections=${commons-collections.version}, \
+          org.apache.commons.logging=${commons-logging.version}, \
+
+description = lists all agents for the current host
+
+usage = list-agents [-d <url>] [-l <level>]
+
+# This command does not have any options
+options = AUTO_DB_OPTIONS, AUTO_LOG_OPTION
+
+environments = cli, shell
--- a/killvm/command/pom.xml	Wed Oct 15 14:46:57 2014 -0400
+++ b/killvm/command/pom.xml	Thu Oct 16 09:41:45 2014 -0400
@@ -92,11 +92,6 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>org.osgi</groupId>
-      <artifactId>org.osgi.compendium</artifactId>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
       <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-common-core</artifactId>
       <version>${project.version}</version>
@@ -127,11 +122,6 @@
       <artifactId>thermostat-shared-config</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-client-core</artifactId>
-      <version>${project.version}</version>
-    </dependency>
   </dependencies>
 
 </project>
--- a/killvm/command/src/main/java/com/redhat/thermostat/killvm/command/KillVMCommand.java	Wed Oct 15 14:46:57 2014 -0400
+++ b/killvm/command/src/main/java/com/redhat/thermostat/killvm/command/KillVMCommand.java	Thu Oct 16 09:41:45 2014 -0400
@@ -46,7 +46,6 @@
 import com.redhat.thermostat.common.command.Request;
 import com.redhat.thermostat.killvm.command.internal.ShellVMKilledListener;
 import com.redhat.thermostat.killvm.command.locale.LocaleResources;
-import com.redhat.thermostat.shared.locale.LocalizedString;
 import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.storage.core.HostRef;
 import com.redhat.thermostat.storage.core.VmRef;
@@ -87,12 +86,6 @@
         attemptToKillVM(args.getVM());
     }
 
-    private void requireNotNull(Object object, LocalizedString message) throws CommandException {
-        if (object == null) {
-            throw new CommandException(message);
-        }
-    }
-
     private void attemptToKillVM(VmRef vmRef) throws CommandException {
         VmInfo result = vmInfoDAO.getVmInfo(vmRef);