changeset 621:210694fa00fe

Show list of agents and backends on the client Modify the 'agent configuration' dialog on the client to show (but not modify) agent information (including backends the agent has). Reviewed-by: rkennke Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-September/003216.html
author Omair Majid <omajid@redhat.com>
date Tue, 18 Sep 2012 12:43:05 -0400
parents cb5aa245e81f
children d771115988ea
files client/core/src/main/java/com/redhat/thermostat/client/internal/AgentConfigurationSource.java client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java client/core/src/main/java/com/redhat/thermostat/client/internal/SwingViewFactory.java client/core/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java client/core/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationController.java client/core/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationFrame.java client/core/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationModel.java client/core/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationView.java client/core/src/main/java/com/redhat/thermostat/client/ui/AgentInformationDisplayController.java client/core/src/main/java/com/redhat/thermostat/client/ui/AgentInformationDisplayFrame.java client/core/src/main/java/com/redhat/thermostat/client/ui/AgentInformationDisplayModel.java client/core/src/main/java/com/redhat/thermostat/client/ui/AgentInformationDisplayView.java client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java client/core/src/main/resources/com/redhat/thermostat/client/locale/strings.properties client/core/src/test/java/com/redhat/thermostat/client/internal/SwingViewFactoryTest.java client/core/src/test/java/com/redhat/thermostat/client/ui/AgentConfigurationControllerTest.java client/core/src/test/java/com/redhat/thermostat/client/ui/AgentConfigurationFrameTest.java client/core/src/test/java/com/redhat/thermostat/client/ui/AgentInformationDisplayControllerTest.java client/core/src/test/java/com/redhat/thermostat/client/ui/AgentInformationDisplayFrameTest.java client/core/src/test/java/com/redhat/thermostat/client/ui/AgentInformationDisplayModelTest.java
diffstat 20 files changed, 1429 insertions(+), 1068 deletions(-) [+]
line wrap: on
line diff
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/AgentConfigurationSource.java	Tue Sep 18 16:24:54 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.internal;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class AgentConfigurationSource {
-
-    // FIXME fix this properly
-
-    public List<String> getKnownAgents() {
-        return Arrays.asList(new String[] { "Agent Smith", "Agent Jones", "Agent Brown" });
-    }
-
-    public Map<String, Boolean> getAgentBackends(String agentName) {
-        Map<String, Boolean> fake = new HashMap<>();
-        fake.put("Monitor New JVMs", true);
-        fake.put("Use up all my CPU Cycles", false);
-        return fake;
-    }
-
-    public void updateAgentConfig(String agentName, Map<String, Boolean> newBackendStatus) {
-        // TODO Auto-generated method stub
-    }
-
-}
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java	Tue Sep 18 16:24:54 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java	Tue Sep 18 12:43:05 2012 -0400
@@ -57,9 +57,9 @@
 import com.redhat.thermostat.client.osgi.service.VmDecorator;
 import com.redhat.thermostat.client.osgi.service.VMContextAction;
 import com.redhat.thermostat.client.osgi.service.VmFilter;
-import com.redhat.thermostat.client.ui.AgentConfigurationController;
-import com.redhat.thermostat.client.ui.AgentConfigurationModel;
-import com.redhat.thermostat.client.ui.AgentConfigurationView;
+import com.redhat.thermostat.client.ui.AgentInformationDisplayController;
+import com.redhat.thermostat.client.ui.AgentInformationDisplayModel;
+import com.redhat.thermostat.client.ui.AgentInformationDisplayView;
 import com.redhat.thermostat.client.ui.ClientConfigurationController;
 import com.redhat.thermostat.client.ui.ClientConfigurationView;
 import com.redhat.thermostat.client.ui.HostInformationController;
@@ -403,10 +403,9 @@
     }
 
     private void showAgentConfiguration() {
-        AgentConfigurationSource agentPrefs = new AgentConfigurationSource();
-        AgentConfigurationModel model = new AgentConfigurationModel(agentPrefs);
-        AgentConfigurationView view = ApplicationContext.getInstance().getViewFactory().getView(AgentConfigurationView.class);
-        AgentConfigurationController controller = new AgentConfigurationController(model, view);
+        AgentInformationDisplayModel model = new AgentInformationDisplayModel();
+        AgentInformationDisplayView view = ApplicationContext.getInstance().getViewFactory().getView(AgentInformationDisplayView.class);
+        AgentInformationDisplayController controller = new AgentInformationDisplayController(model, view);
         controller.showView();
     }
 
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/SwingViewFactory.java	Tue Sep 18 16:24:54 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/SwingViewFactory.java	Tue Sep 18 12:43:05 2012 -0400
@@ -41,8 +41,8 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.client.ui.AgentConfigurationFrame;
-import com.redhat.thermostat.client.ui.AgentConfigurationView;
+import com.redhat.thermostat.client.ui.AgentInformationDisplayFrame;
+import com.redhat.thermostat.client.ui.AgentInformationDisplayView;
 import com.redhat.thermostat.client.ui.ClientConfigurationSwing;
 import com.redhat.thermostat.client.ui.ClientConfigurationView;
 import com.redhat.thermostat.client.ui.HostCpuPanel;
@@ -73,7 +73,7 @@
     private static final Logger logger = LoggingUtils.getLogger(SwingViewFactory.class);
 
     public SwingViewFactory() {
-        setViewClass(AgentConfigurationView.class, AgentConfigurationFrame.class);
+        setViewClass(AgentInformationDisplayView.class, AgentInformationDisplayFrame.class);
         setViewClass(ClientConfigurationView.class, ClientConfigurationSwing.class);
 
         setViewClass(SummaryView.class, SummaryPanel.class);
--- a/client/core/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java	Tue Sep 18 16:24:54 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java	Tue Sep 18 12:43:05 2012 -0400
@@ -36,6 +36,8 @@
 
 package com.redhat.thermostat.client.locale;
 
+import com.redhat.thermostat.client.ui.ValueField;
+
 public enum LocaleResources {
 
     MISSING_INFO,
@@ -57,9 +59,10 @@
     MENU_FILE,
     MENU_FILE_EXIT,
     MENU_EDIT,
-    MENU_EDIT_CONFIGURE_AGENT,
     MENU_EDIT_CONFIGURE_CLIENT,
     MENU_EDIT_ENABLE_HISTORY_MODE,
+    MENU_VIEW,
+    MENU_VIEW_AGENTS,
     MENU_HELP,
     MENU_HELP_ABOUT,
 
@@ -181,9 +184,21 @@
     VM_CLASSES_CHART_REAL_TIME_LABEL,
     VM_CLASSES_CHART_LOADED_CLASSES_LABEL,
 
-    CONFIGURE_AGENT_WINDOW_TITLE,
-    CONFIGURE_AGENT_AGENTS_LIST,
-    CONFIGURE_ENABLE_BACKENDS,
+    AGENT_INFO_WINDOW_TITLE,
+    AGENT_INFO_AGENTS_LIST,
+    AGENT_INFO_AGENT_SECTION_TITLE,
+    AGENT_INFO_AGENT_NAME_LABEL,
+    AGENT_INFO_AGENT_ID_LABEL,
+    AGENT_INFO_AGENT_COMMAND_ADDRESS_LABEL,
+    AGENT_INFO_AGENT_START_TIME_LABEL,
+    AGENT_INFO_AGENT_STOP_TIME_LABEL,
+    AGENT_INFO_AGENT_RUNNING,
+    AGENT_INFO_BACKENDS_SECTION_TITLE,
+    AGENT_INFO_BACKEND_NAME_COLUMN,
+    AGENT_INFO_BACKEND_STATUS_COLUMN,
+    AGENT_INFO_BACKEND_STATUS_ACTIVE,
+    AGENT_INFO_BACKEND_STATUS_INACTIVE,
+    AGENT_INFO_BACKEND_DESCRIPTION_LABEL,
 
     CLIENT_PREFS_WINDOW_TITLE,
     CLIENT_PREFS_CONNECTION,
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationController.java	Tue Sep 18 16:24:54 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.ui;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import com.redhat.thermostat.client.ui.AgentConfigurationView.ConfigurationAction;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-
-public class AgentConfigurationController implements ActionListener<ConfigurationAction> {
-
-    private final AgentConfigurationView view;
-    private final AgentConfigurationModel model;
-    private String agentName = null;
-
-    public AgentConfigurationController(AgentConfigurationModel model, AgentConfigurationView view) {
-        this.view = view;
-        this.model = model;
-
-        view.addActionListener(this);
-
-    }
-
-    public void showView() {
-        Collection<String> agents = model.getAgents();
-        agentName = null;
-        for (String agentName: agents) {
-            if (this.agentName == null) {
-                this.agentName = agentName;
-            }
-            view.addAgent(agentName);
-        }
-        view.showDialog();
-        updateViewFromModel();
-    }
-
-    public void hideView() {
-        view.hideDialog();
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent<ConfigurationAction> actionEvent) {
-        switch (actionEvent.getActionId()) {
-            case SWITCH_AGENT:
-                updateModelFromCurrentView();
-                String agentName = view.getSelectedAgent();
-                this.agentName = agentName;
-                updateViewFromModel();
-                break;
-            case CLOSE_ACCEPT:
-                updateModelFromCurrentView();
-                model.saveConfiguration();
-                /* fall through */
-            case CLOSE_CANCEL:
-                hideView();
-                break;
-            default:
-                throw new IllegalArgumentException("unknown event");
-        }
-    }
-
-    private void updateModelFromCurrentView() {
-        Map<String, Boolean> map = view.getBackendStatus();
-        for (Entry<String, Boolean> entry: map.entrySet()) {
-            model.setBackendEnabled(agentName, entry.getKey(), entry.getValue());
-        }
-
-    }
-
-    private void updateViewFromModel() {
-        Map<String, Boolean> map = new HashMap<>();
-        for (String backendName: model.getBackends(agentName)) {
-            map.put(backendName, model.getAgentBackendEnabled(agentName, backendName));
-        }
-        view.setBackendStatus(map);
-
-    }
-}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationFrame.java	Tue Sep 18 16:24:54 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,321 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.ui;
-
-import static com.redhat.thermostat.client.locale.Translate.localize;
-
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import javax.swing.Box;
-import javax.swing.DefaultListModel;
-import javax.swing.GroupLayout;
-import javax.swing.GroupLayout.Alignment;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.LayoutStyle.ComponentPlacement;
-import javax.swing.SwingUtilities;
-
-import com.redhat.thermostat.client.locale.LocaleResources;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import javax.swing.JScrollPane;
-import javax.swing.JList;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-
-public class AgentConfigurationFrame extends JFrame implements AgentConfigurationView {
-
-    private static final long serialVersionUID = -6049272471909474886L;
-
-    private final CopyOnWriteArrayList<ActionListener<ConfigurationAction>> listeners = new CopyOnWriteArrayList<>();
-
-    private final Map<String, JCheckBox> backends = Collections.synchronizedMap(new HashMap<String, JCheckBox>());
-
-    private final JPanel availableBackendsPanel;
-    private final GridBagConstraints availableBackendsPanelContstraints = new GridBagConstraints();
-
-    private final ConfigurationCompleteListener configurationComplete;
-    private final AgentChangedListener agentChanged;
-    private final WindowClosingListener windowListener;
-
-    private final JButton okayButton;
-    private final JButton cancelButton;
-
-    private final JList<String> agentList;
-    private final DefaultListModel<String> listModel;
-
-
-    public AgentConfigurationFrame() {
-        assertInEDT();
-
-        configurationComplete = new ConfigurationCompleteListener();
-        agentChanged = new AgentChangedListener();
-        windowListener = new WindowClosingListener();
-
-        setTitle(localize(LocaleResources.CONFIGURE_AGENT_WINDOW_TITLE));
-        addWindowListener(windowListener);
-
-        JLabel lblEnabledisableBackends = new JLabel(localize(LocaleResources.CONFIGURE_ENABLE_BACKENDS));
-
-        availableBackendsPanel = new JPanel();
-
-        okayButton = new JButton(localize(LocaleResources.BUTTON_OK));
-        okayButton.addActionListener(configurationComplete);
-
-        cancelButton = new JButton(localize(LocaleResources.BUTTON_CANCEL));
-        cancelButton.addActionListener(configurationComplete);
-
-        JScrollPane scrollPane = new JScrollPane();
-
-        JLabel lblAgents = new JLabel(localize(LocaleResources.CONFIGURE_AGENT_AGENTS_LIST));
-
-        GroupLayout groupLayout = new GroupLayout(getContentPane());
-        groupLayout.setHorizontalGroup(
-            groupLayout.createParallelGroup(Alignment.LEADING)
-                .addGroup(groupLayout.createSequentialGroup()
-                    .addContainerGap()
-                    .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
-                        .addGroup(groupLayout.createSequentialGroup()
-                            .addComponent(scrollPane, GroupLayout.PREFERRED_SIZE, 127, GroupLayout.PREFERRED_SIZE)
-                            .addGap(0)
-                            .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
-                                .addGroup(Alignment.TRAILING, groupLayout.createSequentialGroup()
-                                    .addComponent(cancelButton)
-                                    .addPreferredGap(ComponentPlacement.RELATED)
-                                    .addComponent(okayButton))
-                                .addGroup(groupLayout.createSequentialGroup()
-                                    .addGap(12)
-                                    .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
-                                        .addGroup(groupLayout.createSequentialGroup()
-                                            .addGap(12)
-                                            .addComponent(availableBackendsPanel, GroupLayout.DEFAULT_SIZE, 540, Short.MAX_VALUE))
-                                        .addComponent(lblEnabledisableBackends)))))
-                        .addComponent(lblAgents))
-                    .addContainerGap())
-        );
-        groupLayout.setVerticalGroup(
-            groupLayout.createParallelGroup(Alignment.TRAILING)
-                .addGroup(groupLayout.createSequentialGroup()
-                    .addGap(6)
-                    .addComponent(lblAgents)
-                    .addPreferredGap(ComponentPlacement.UNRELATED)
-                    .addGroup(groupLayout.createParallelGroup(Alignment.TRAILING)
-                        .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 413, Short.MAX_VALUE)
-                        .addGroup(groupLayout.createSequentialGroup()
-                            .addComponent(lblEnabledisableBackends)
-                            .addGap(2)
-                            .addComponent(availableBackendsPanel, GroupLayout.DEFAULT_SIZE, 365, Short.MAX_VALUE)
-                            .addPreferredGap(ComponentPlacement.RELATED)
-                            .addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
-                                .addComponent(okayButton)
-                                .addComponent(cancelButton))))
-                    .addContainerGap())
-        );
-
-        listModel = new DefaultListModel<String>();
-        agentList = new JList<String>(listModel);
-        agentList.setName("agentList");
-        agentList.addListSelectionListener(agentChanged);
-        scrollPane.setViewportView(agentList);
-
-        availableBackendsPanel.setLayout(new GridBagLayout());
-        getContentPane().setLayout(groupLayout);
-
-    }
-
-    private void resetConstraints() {
-        availableBackendsPanelContstraints.gridwidth = 1;
-        availableBackendsPanelContstraints.gridy = 0;
-        availableBackendsPanelContstraints.gridx = 0;
-        availableBackendsPanelContstraints.weightx = 0;
-        availableBackendsPanelContstraints.weighty = 0;
-        availableBackendsPanelContstraints.anchor = GridBagConstraints.LINE_START;
-        availableBackendsPanelContstraints.fill = GridBagConstraints.BOTH;
-    }
-
-
-    @Override
-    public void addActionListener(ActionListener<ConfigurationAction> listener) {
-        listeners.add(listener);
-    }
-
-    @Override
-    public void removeActionListener(ActionListener<ConfigurationAction> listener) {
-        listeners.remove(listener);
-    }
-
-    @Override
-    public void addAgent(final String agentName) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                listModel.addElement(agentName);
-            }
-        });
-    }
-
-    @Override
-    public String getSelectedAgent() {
-        assertInEDT();
-        return agentList.getSelectedValue();
-    }
-
-    @Override
-    public void clearAllAgents() {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                listModel.clear();
-            }
-        });
-    }
-
-
-    @Override
-    public void setBackendStatus(final Map<String, Boolean> backendStatus) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                backends.clear();
-                availableBackendsPanel.removeAll();
-                resetConstraints();
-
-                for (Entry<String, Boolean> entry: backendStatus.entrySet()) {
-                    String backendName = entry.getKey();
-                    boolean checked = entry.getValue();
-                    JCheckBox checkBox = new JCheckBox(backendName);
-                    checkBox.setSelected(checked);
-                    checkBox.setActionCommand(backendName);
-                    backends.put(backendName, checkBox);
-                    availableBackendsPanel.add(checkBox, availableBackendsPanelContstraints);
-                    availableBackendsPanelContstraints.gridy++;
-                }
-                availableBackendsPanelContstraints.weighty = 1.0;
-                availableBackendsPanelContstraints.weightx = 1.0;
-                availableBackendsPanelContstraints.fill = GridBagConstraints.BOTH;
-                availableBackendsPanel.add(Box.createGlue(), availableBackendsPanelContstraints);
-                AgentConfigurationFrame.this.revalidate();
-            }
-        });
-    }
-
-    @Override
-    public Map<String, Boolean> getBackendStatus() {
-        assertInEDT();
-
-        Map<String,Boolean> latestUserSpecified = new HashMap<>();
-        for (Entry<String, JCheckBox> entry: backends.entrySet()) {
-            latestUserSpecified.put(entry.getKey(), entry.getValue().isSelected());
-        }
-        return latestUserSpecified;
-    }
-
-    @Override
-    public void showDialog() {
-        assertInEDT();
-
-        pack();
-        setVisible(true);
-
-        agentList.setSelectedIndex(0);
-    }
-
-    @Override
-    public void hideDialog() {
-        assertInEDT();
-
-        setVisible(false);
-        dispose();
-    }
-
-    private void fireAction(ActionEvent<ConfigurationAction> actionEvent) {
-        for (ActionListener<ConfigurationAction> l: listeners) {
-            l.actionPerformed(actionEvent);
-        }
-    }
-
-    private static void assertInEDT() {
-        if (!SwingUtilities.isEventDispatchThread()) {
-            throw new IllegalStateException("must be called from within the swing EDT");
-        }
-    }
-
-    class ConfigurationCompleteListener implements java.awt.event.ActionListener {
-        @Override
-        public void actionPerformed(java.awt.event.ActionEvent e) {
-            Object source = e.getSource();
-            if (source == okayButton) {
-                fireAction(new ActionEvent<>(AgentConfigurationFrame.this, ConfigurationAction.CLOSE_ACCEPT));
-            } else if (source == cancelButton) {
-                fireAction(new ActionEvent<>(AgentConfigurationFrame.this, ConfigurationAction.CLOSE_CANCEL));
-            }
-        }
-    }
-
-    class WindowClosingListener extends WindowAdapter {
-        @Override
-        public void windowClosing(WindowEvent e) {
-            fireAction(new ActionEvent<>(AgentConfigurationFrame.this, ConfigurationAction.CLOSE_CANCEL));
-        }
-    }
-
-    class AgentChangedListener implements ListSelectionListener {
-        @Override
-        public void valueChanged(ListSelectionEvent e) {
-            if (e.getSource() == agentList) {
-                if (e.getValueIsAdjusting()) {
-                    return;
-                }
-                fireAction(new ActionEvent<>(AgentConfigurationFrame.this, ConfigurationAction.SWITCH_AGENT));
-            } else {
-                throw new IllegalStateException("unknown trigger");
-            }
-        }
-    }
-
-}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationModel.java	Tue Sep 18 16:24:54 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.ui;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import com.redhat.thermostat.client.internal.AgentConfigurationSource;
-
-/**
- * This model sits between the current view and the remote model, and allows
- * us to make changes and later throw them away.
- */
-public class AgentConfigurationModel {
-
-    private final AgentConfigurationSource remoteConfiguration;
-
-    private final List<String> knownAgents;
-    private Map<String, Map<String, Boolean>> enabledBackends;
-
-    public AgentConfigurationModel(AgentConfigurationSource configSource) {
-        this.remoteConfiguration = configSource;
-
-        knownAgents = new ArrayList<>(remoteConfiguration.getKnownAgents());
-        enabledBackends = new HashMap<>();
-        for (String agent: knownAgents) {
-            enabledBackends.put(agent, new HashMap<String, Boolean>(remoteConfiguration.getAgentBackends(agent)));
-        }
-    }
-
-    public Collection<String> getAgents() {
-        return Collections.unmodifiableList(knownAgents);
-    }
-
-    public Collection<String> getBackends(String agentName) {
-        return Collections.unmodifiableSet(enabledBackends.get(agentName).keySet());
-    }
-
-    public void setBackendEnabled(String agentName, String backendName, boolean enabled) {
-        enabledBackends.get(agentName).put(backendName, enabled);
-    }
-
-    public boolean getAgentBackendEnabled(String agentName, String backendName) {
-        return enabledBackends.get(agentName).get(backendName);
-    }
-
-    public void saveConfiguration() {
-        for (Entry<String, Map<String, Boolean>> entry: enabledBackends.entrySet()) {
-            remoteConfiguration.updateAgentConfig(entry.getKey(), entry.getValue());
-        }
-    }
-
-}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationView.java	Tue Sep 18 16:24:54 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.ui;
-
-import java.util.Map;
-
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.View;
-
-public interface AgentConfigurationView extends View {
-
-    enum ConfigurationAction {
-        SWITCH_AGENT,
-        CLOSE_ACCEPT,
-        CLOSE_CANCEL,
-    }
-
-    void showDialog();
-
-    void hideDialog();
-
-    void addActionListener(ActionListener<ConfigurationAction> listener);
-
-    void removeActionListener(ActionListener<ConfigurationAction> listener);
-
-    void addAgent(String agentName);
-
-    String getSelectedAgent();
-
-    void clearAllAgents();
-
-    void setBackendStatus(Map<String,Boolean> agentConfiguration);
-
-    Map<String,Boolean> getBackendStatus();
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/AgentInformationDisplayController.java	Tue Sep 18 12:43:05 2012 -0400
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static com.redhat.thermostat.client.locale.Translate.localize;
+
+import java.text.DateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.redhat.thermostat.client.locale.LocaleResources;
+import com.redhat.thermostat.client.ui.AgentInformationDisplayView.ConfigurationAction;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.model.AgentInformation;
+import com.redhat.thermostat.common.model.BackendInformation;
+
+public class AgentInformationDisplayController implements ActionListener<ConfigurationAction> {
+
+    private final AgentInformationDisplayView view;
+    private final AgentInformationDisplayModel model;
+
+    private final DateFormat dateTimeFormat;
+
+    public AgentInformationDisplayController(AgentInformationDisplayModel model, AgentInformationDisplayView view) {
+        this.view = view;
+        this.model = model;
+
+        view.addConfigurationListener(this);
+
+        dateTimeFormat = DateFormat.getDateTimeInstance();
+    }
+
+    public void showView() {
+        Collection<AgentInformation> agents = model.getAgents();
+        String agentId = null;
+        for (AgentInformation agentInfo : agents) {
+            String agentName = agentInfo.getAgentId();
+            if (agentId == null) {
+                agentId = agentInfo.getAgentId();
+            }
+            view.addAgent(agentName);
+        }
+        view.showDialog();
+        updateViewFromModel(agentId);
+    }
+
+    public void hideView() {
+        view.hideDialog();
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent<ConfigurationAction> actionEvent) {
+        String agentId = view.getSelectedAgent();
+        switch (actionEvent.getActionId()) {
+        case SWITCH_AGENT:
+            updateViewFromModel(agentId);
+            break;
+        case SHOW_BACKEND_DESCRIPTION:
+            String backendName = (String) actionEvent.getPayload();
+            view.setSelectedAgentBackendDescription(getBackendDescription(agentId, backendName));
+            break;
+        case CLOSE:
+            hideView();
+            break;
+        default:
+            throw new IllegalArgumentException("unknown event");
+        }
+    }
+
+    private String getBackendDescription(String agentId, String backendName) {
+        return getBackendInformation(agentId, backendName).getDescription();
+    }
+
+    private BackendInformation getBackendInformation(String agentId, String backendName) {
+        Collection<BackendInformation> backendInfos = model.getBackends(agentId);
+        for (BackendInformation backendInfo : backendInfos) {
+            if (backendInfo.getName().equals(backendName)) {
+                return backendInfo;
+            }
+        }
+        return null;
+    }
+
+    private void updateViewFromModel(String agentId) {
+        AgentInformation agentInfo = model.getAgentInfo(agentId);
+        view.setSelectedAgentName(agentInfo.getAgentId());
+        view.setSelectedAgentId(agentInfo.getAgentId());
+        view.setSelectedAgentCommandAddress(agentInfo.getConfigListenAddress());
+        long startTime = agentInfo.getStartTime();
+        view.setSelectedAgentStartTime(dateTimeFormat.format(new Date(startTime)));
+        long stopTime = agentInfo.getStopTime();
+        if (stopTime >= startTime) {
+            view.setSelectedAgentStopTime(dateTimeFormat.format(new Date(stopTime)));
+        } else {
+            view.setSelectedAgentStopTime(localize(LocaleResources.AGENT_INFO_AGENT_RUNNING));
+        }
+
+        Map<String, String> map = new HashMap<>();
+        for (BackendInformation backendInfo : model.getBackends(agentId)) {
+            String status = backendInfo.isActive() ?
+                    localize(LocaleResources.AGENT_INFO_BACKEND_STATUS_ACTIVE) : localize(LocaleResources.AGENT_INFO_BACKEND_STATUS_INACTIVE);
+            map.put(backendInfo.getName(), status);
+        }
+        view.setSelectedAgentBackendStatus(map);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/AgentInformationDisplayFrame.java	Tue Sep 18 12:43:05 2012 -0400
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static com.redhat.thermostat.client.locale.Translate.localize;
+
+import java.awt.BorderLayout;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.swing.DefaultListModel;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.DefaultTableModel;
+
+import com.redhat.thermostat.client.locale.LocaleResources;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+
+public class AgentInformationDisplayFrame extends AgentInformationDisplayView {
+
+    private static final String[] BACKEND_TABLE_COLUMN_NAMES = new String[] {
+        localize(LocaleResources.AGENT_INFO_BACKEND_NAME_COLUMN),
+        localize(LocaleResources.AGENT_INFO_BACKEND_STATUS_COLUMN),
+    };
+
+    private final CopyOnWriteArrayList<ActionListener<ConfigurationAction>> listeners = new CopyOnWriteArrayList<>();
+
+    private final JFrame frame;
+
+    private final ConfigurationCompleteListener configurationComplete;
+    private final AgentChangedListener agentChanged;
+    private final WindowClosingListener windowListener;
+
+    private final JButton closeButton;
+
+    private final JList<String> agentList;
+    private final DefaultListModel<String> listModel;
+
+    private final ValueField currentAgentName;
+    private final ValueField currentAgentId;
+    private final ValueField currentAgentCommandAddress;
+    private final ValueField currentAgentStartTime;
+    private final ValueField currentAgentStopTime;
+
+    private final JTable backendsTable;
+    private final DefaultTableModel backendsTableModel;
+    private final ValueField backendDescription;
+
+    public AgentInformationDisplayFrame() {
+        assertInEDT();
+
+        configurationComplete = new ConfigurationCompleteListener();
+        agentChanged = new AgentChangedListener();
+        windowListener = new WindowClosingListener();
+
+        frame = new JFrame();
+        frame.setTitle(localize(LocaleResources.AGENT_INFO_WINDOW_TITLE));
+        frame.addWindowListener(windowListener);
+
+        closeButton = new JButton(localize(LocaleResources.BUTTON_CLOSE));
+        closeButton.addActionListener(configurationComplete);
+        closeButton.setName("close");
+
+        JSplitPane splitPane = new JSplitPane();
+        splitPane.setResizeWeight(0.35);
+
+        GroupLayout mainLayout = new GroupLayout(frame.getContentPane());
+        mainLayout.setHorizontalGroup(
+            mainLayout.createParallelGroup(Alignment.TRAILING)
+                .addGroup(mainLayout.createSequentialGroup()
+                    .addContainerGap()
+                    .addGroup(mainLayout.createParallelGroup(Alignment.TRAILING)
+                        .addComponent(splitPane, GroupLayout.DEFAULT_SIZE, 664, Short.MAX_VALUE)
+                        .addComponent(closeButton))
+                    .addContainerGap()));
+
+        mainLayout.setVerticalGroup(
+            mainLayout.createParallelGroup(Alignment.TRAILING)
+                .addGroup(mainLayout.createSequentialGroup()
+                    .addContainerGap()
+                    .addComponent(splitPane, GroupLayout.DEFAULT_SIZE, 472, Short.MAX_VALUE)
+                    .addPreferredGap(ComponentPlacement.UNRELATED)
+                    .addComponent(closeButton)
+                    .addContainerGap()));
+
+        JPanel agentListPanel = new JPanel();
+        splitPane.setLeftComponent(agentListPanel);
+
+        JLabel agentLabel = new JLabel(localize(LocaleResources.AGENT_INFO_AGENTS_LIST));
+
+        JScrollPane scrollPane = new JScrollPane();
+
+        listModel = new DefaultListModel<String>();
+        agentList = new JList<String>(listModel);
+        agentList.setName("agentList");
+        agentList.addListSelectionListener(agentChanged);
+        agentListPanel.setLayout(new BorderLayout());
+
+        scrollPane.setViewportView(agentList);
+        agentListPanel.add(scrollPane);
+        agentListPanel.add(agentLabel, BorderLayout.NORTH);
+
+        JPanel agentConfigurationPanel = new JPanel();
+        splitPane.setRightComponent(agentConfigurationPanel);
+
+        SectionHeader agentSectionTitle = new SectionHeader(localize(LocaleResources.AGENT_INFO_AGENT_SECTION_TITLE));
+
+        LabelField agentNameLabel = new LabelField(localize(LocaleResources.AGENT_INFO_AGENT_NAME_LABEL));
+        LabelField agentIdLabel = new LabelField(localize(LocaleResources.AGENT_INFO_AGENT_ID_LABEL));
+        LabelField agentConfigurationAddressLabel = new LabelField(localize(LocaleResources.AGENT_INFO_AGENT_COMMAND_ADDRESS_LABEL));
+        LabelField agentStartTimeLabel = new LabelField(localize(LocaleResources.AGENT_INFO_AGENT_START_TIME_LABEL));
+        LabelField agentStopTimeLabel = new LabelField(localize(LocaleResources.AGENT_INFO_AGENT_STOP_TIME_LABEL));
+
+        currentAgentName = new ValueField("${AGENT_NAME}");
+        currentAgentName.setName("agentName");
+        currentAgentId = new ValueField("${AGENT_ID}");
+        currentAgentId.setName("agentId");
+        currentAgentCommandAddress = new ValueField("${AGENT_COMMAND_ADDRESS}");
+        currentAgentCommandAddress.setName("commandAddress");
+        currentAgentStartTime = new ValueField("${START_TIME}");
+        currentAgentStartTime.setName("startTime");
+        currentAgentStopTime = new ValueField("${STOP_TIME}");
+        currentAgentStopTime.setName("stopTime");
+
+        SectionHeader backendSectionTitle = new SectionHeader(localize(LocaleResources.AGENT_INFO_BACKENDS_SECTION_TITLE));
+
+        backendsTableModel = new DefaultTableModel();
+        backendsTableModel.setColumnIdentifiers(BACKEND_TABLE_COLUMN_NAMES);
+
+        backendsTable = new JTable(backendsTableModel);
+        backendsTable.setName("backends");
+        backendsTable.setCellSelectionEnabled(false);
+        backendsTable.setColumnSelectionAllowed(false);
+        backendsTable.setRowSelectionAllowed(true);
+        backendsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        backendsTable.getSelectionModel().addListSelectionListener(new BackendSelectionListener());
+
+        JScrollPane backendsTableScollPane = new JScrollPane(backendsTable);
+
+        JLabel backendDescriptionLabel = new JLabel(localize(LocaleResources.AGENT_INFO_BACKEND_DESCRIPTION_LABEL));
+        backendDescription = new ValueField("${DESCRIPTION}");
+        backendDescription.setName("backendDescription");
+
+        GroupLayout agentConfigurationPanelLayout = new GroupLayout(agentConfigurationPanel);
+        agentConfigurationPanelLayout.setHorizontalGroup(
+            agentConfigurationPanelLayout.createParallelGroup()
+                .addComponent(agentSectionTitle, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addGroup(agentConfigurationPanelLayout.createSequentialGroup()
+                    .addGroup(agentConfigurationPanelLayout.createParallelGroup(Alignment.LEADING, true)
+                        .addComponent(agentNameLabel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(agentIdLabel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(agentConfigurationAddressLabel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(agentStartTimeLabel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(agentStopTimeLabel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                    .addGroup(agentConfigurationPanelLayout.createParallelGroup(Alignment.LEADING, true)
+                        .addComponent(currentAgentName, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
+                        .addComponent(currentAgentId, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
+                        .addComponent(currentAgentCommandAddress, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
+                        .addComponent(currentAgentStartTime, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
+                        .addComponent(currentAgentStopTime, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)))
+                .addComponent(backendSectionTitle, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
+                .addComponent(backendsTableScollPane, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
+                .addComponent(backendDescriptionLabel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addComponent(backendDescription, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE));
+
+        agentConfigurationPanelLayout.setVerticalGroup(
+            agentConfigurationPanelLayout.createSequentialGroup()
+                .addComponent(agentSectionTitle)
+                .addGroup(agentConfigurationPanelLayout.createParallelGroup(Alignment.BASELINE, false)
+                    .addComponent(agentNameLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(currentAgentName, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addGroup(agentConfigurationPanelLayout.createParallelGroup(Alignment.BASELINE, false)
+                    .addComponent(agentIdLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(currentAgentId, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addGroup(agentConfigurationPanelLayout.createParallelGroup(Alignment.BASELINE, false)
+                    .addComponent(agentConfigurationAddressLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(currentAgentCommandAddress, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addGroup(agentConfigurationPanelLayout.createParallelGroup(Alignment.BASELINE, false)
+                    .addComponent(agentStartTimeLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(currentAgentStartTime, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addGroup(agentConfigurationPanelLayout.createParallelGroup(Alignment.BASELINE, false)
+                    .addComponent(agentStopTimeLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(currentAgentStopTime, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addComponent(backendSectionTitle)
+                .addComponent(backendsTableScollPane)
+                .addComponent(backendDescriptionLabel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE)
+                .addComponent(backendDescription, 30, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE));
+
+        agentConfigurationPanelLayout.setAutoCreateGaps(true);
+        agentConfigurationPanelLayout.setAutoCreateContainerGaps(true);
+        agentConfigurationPanel.setLayout(agentConfigurationPanelLayout);
+
+        frame.getContentPane().setLayout(mainLayout);
+
+    }
+
+    @Override
+    public void addConfigurationListener(ActionListener<ConfigurationAction> listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeConfigurationListener(ActionListener<ConfigurationAction> listener) {
+        listeners.remove(listener);
+    }
+
+    @Override
+    public void addAgent(final String agentName) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                listModel.addElement(agentName);
+                if (agentList.getSelectedIndex() == -1) {
+                    agentList.setSelectedIndex(0);
+                }
+            }
+        });
+    }
+
+    @Override
+    public String getSelectedAgent() {
+        assertInEDT();
+        return agentList.getSelectedValue();
+    }
+
+    @Override
+    public void clearAllAgents() {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                listModel.clear();
+            }
+        });
+    }
+
+    @Override
+    public void setSelectedAgentName(final String agentName) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                currentAgentName.setText(agentName);
+            }
+        });
+    }
+
+    @Override
+    public void setSelectedAgentId(final String agentId) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                currentAgentId.setText(agentId);
+            }
+        });
+    }
+
+    @Override
+    public void setSelectedAgentCommandAddress(final String address) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                currentAgentCommandAddress.setText(address);
+            }
+        });
+    }
+
+    @Override
+    public void setSelectedAgentStartTime(final String startTime) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                currentAgentStartTime.setText(startTime);
+            }
+        });
+    }
+
+    @Override
+    public void setSelectedAgentStopTime(final String stopTime) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                currentAgentStopTime.setText(stopTime);
+            }
+        });
+    }
+
+    @Override
+    public void setSelectedAgentBackendStatus(final Map<String, String> backendStatus) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                int i = 0;
+                for (Entry<String, String> entry : backendStatus.entrySet()) {
+                    String backendName = entry.getKey();
+                    String status = entry.getValue();
+                    int rowCount = backendsTableModel.getRowCount();
+                    if (i >= rowCount) {
+                        Object[] rowData = new String[] { backendName, status };
+                        backendsTableModel.insertRow(i, rowData);
+                    } else {
+                        backendsTableModel.setValueAt(backendName, i, 0);
+                        backendsTableModel.setValueAt(status, i, 1);
+                    }
+                    i++;
+                }
+
+                if (backendsTable.getRowCount() > 0 && backendsTable.getSelectedRow() == -1) {
+                    backendsTable.setRowSelectionInterval(0, 0);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setSelectedAgentBackendDescription(final String description) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                backendDescription.setText(description);
+            }
+        });
+    }
+
+    @Override
+    public void showDialog() {
+        assertInEDT();
+
+        frame.pack();
+        frame.setVisible(true);
+
+        agentList.setSelectedIndex(0);
+    }
+
+    @Override
+    public void hideDialog() {
+        assertInEDT();
+
+        frame.setVisible(false);
+        frame.dispose();
+    }
+
+    /** This is for tests only */
+    JFrame getFrame() {
+        return frame;
+    }
+
+    private void fireAction(ActionEvent<ConfigurationAction> actionEvent) {
+        for (ActionListener<ConfigurationAction> l : listeners) {
+            l.actionPerformed(actionEvent);
+        }
+    }
+
+    private static void assertInEDT() {
+        if (!SwingUtilities.isEventDispatchThread()) {
+            throw new IllegalStateException("must be called from within the swing EDT");
+        }
+    }
+
+    private class ConfigurationCompleteListener implements java.awt.event.ActionListener {
+        @Override
+        public void actionPerformed(java.awt.event.ActionEvent e) {
+            Object source = e.getSource();
+            if (source == closeButton) {
+                fireAction(new ActionEvent<>(AgentInformationDisplayFrame.this, ConfigurationAction.CLOSE));
+            }
+        }
+    }
+
+    private class WindowClosingListener extends WindowAdapter {
+        @Override
+        public void windowClosing(WindowEvent e) {
+            fireAction(new ActionEvent<>(AgentInformationDisplayFrame.this, ConfigurationAction.CLOSE));
+        }
+    }
+
+    private class AgentChangedListener implements ListSelectionListener {
+        @Override
+        public void valueChanged(ListSelectionEvent e) {
+            if (e.getSource() == agentList) {
+                if (e.getValueIsAdjusting()) {
+                    return;
+                }
+                fireAction(new ActionEvent<>(AgentInformationDisplayFrame.this, ConfigurationAction.SWITCH_AGENT));
+            } else {
+                throw new IllegalStateException("unknown trigger");
+            }
+        }
+    }
+
+    private class BackendSelectionListener implements ListSelectionListener {
+
+        @Override
+        public void valueChanged(ListSelectionEvent e) {
+            if (e.getValueIsAdjusting()) {
+                return;
+            }
+
+            int rowIndex = e.getFirstIndex();
+            String backendName = (String) backendsTableModel.getValueAt(rowIndex, 0);
+            ActionEvent<ConfigurationAction> event = new ActionEvent<>(AgentInformationDisplayFrame.this,
+                    ConfigurationAction.SHOW_BACKEND_DESCRIPTION);
+            event.setPayload(backendName);
+            fireAction(event);
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/AgentInformationDisplayModel.java	Tue Sep 18 12:43:05 2012 -0400
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.dao.AgentInfoDAO;
+import com.redhat.thermostat.common.dao.BackendInfoDAO;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.model.AgentInformation;
+import com.redhat.thermostat.common.model.BackendInformation;
+
+/**
+ * This model sits between the current view and the remote model, and maintains
+ * a state of the agents that it knows about.
+ */
+public class AgentInformationDisplayModel {
+
+    private final AgentInfoDAO agentInfoDao;
+    private final BackendInfoDAO backendInfoDao;
+
+    private final List<AgentInformation> agents;
+    private final Map<String, List<BackendInformation>> backends;
+
+    public AgentInformationDisplayModel() {
+        ApplicationContext appContext = ApplicationContext.getInstance();
+        DAOFactory daoFactory = appContext.getDAOFactory();
+        agentInfoDao = daoFactory.getAgentInfoDAO();
+        backendInfoDao = daoFactory.getBackendInfoDAO();
+
+        agents = new ArrayList<>();
+        backends = new HashMap<>();
+
+        refresh();
+    }
+
+    public Collection<AgentInformation> getAgents() {
+        return agents;
+    }
+
+    public AgentInformation getAgentInfo(String agentId) {
+        for (AgentInformation agent : agents) {
+            if (agent.getAgentId().equals(agentId)) {
+                return agent;
+            }
+        }
+        return null;
+    }
+
+    public Collection<BackendInformation> getBackends(String agentId) {
+        return backends.get(agentId);
+    }
+
+    public void refresh() {
+        agents.clear();
+        agents.addAll(agentInfoDao.getAllAgentInformation());
+        backends.clear();
+        for (AgentInformation agent : agents) {
+            String agentId = agent.getAgentId();
+            HostRef agentRef = new HostRef(agentId, agentId);
+            backends.put(agentId, backendInfoDao.getBackendInformation(agentRef));
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/AgentInformationDisplayView.java	Tue Sep 18 12:43:05 2012 -0400
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import java.util.Map;
+
+import com.redhat.thermostat.client.osgi.service.BasicView;
+import com.redhat.thermostat.common.ActionListener;
+
+public abstract class AgentInformationDisplayView extends BasicView {
+
+    enum ConfigurationAction {
+        SWITCH_AGENT,
+        SHOW_BACKEND_DESCRIPTION,
+        CLOSE,
+    }
+
+    public abstract void showDialog();
+
+    public abstract void hideDialog();
+
+    public abstract void addConfigurationListener(ActionListener<ConfigurationAction> listener);
+
+    public abstract void removeConfigurationListener(ActionListener<ConfigurationAction> listener);
+
+    public abstract void addAgent(String agentName);
+
+    public abstract String getSelectedAgent();
+
+    public abstract void clearAllAgents();
+
+    public abstract void setSelectedAgentName(String agentName);
+    public abstract void setSelectedAgentId(String agentId);
+    public abstract void setSelectedAgentCommandAddress(String address);
+    public abstract void setSelectedAgentStartTime(String startTime);
+    public abstract void setSelectedAgentStopTime(String stopTime);
+
+    public abstract void setSelectedAgentBackendStatus(Map<String, String> agentConfiguration);
+    public abstract void setSelectedAgentBackendDescription(String description);
+
+}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Tue Sep 18 16:24:54 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Tue Sep 18 12:43:05 2012 -0400
@@ -405,16 +405,6 @@
         JMenu editMenu = new JMenu(localize(LocaleResources.MENU_EDIT));
         mainMenuBar.add(editMenu);
 
-        JMenuItem configureAgentMenuItem = new JMenuItem(localize(LocaleResources.MENU_EDIT_CONFIGURE_AGENT));
-        configureAgentMenuItem.setName("showAgentConfig");
-        configureAgentMenuItem.addActionListener(new java.awt.event.ActionListener() {
-            @Override
-            public void actionPerformed(java.awt.event.ActionEvent e) {
-                fireViewAction(Action.SHOW_AGENT_CONFIG);
-            }
-        });
-        editMenu.add(configureAgentMenuItem);
-
         JMenuItem configureClientMenuItem = new JMenuItem(localize(LocaleResources.MENU_EDIT_CONFIGURE_CLIENT));
         configureClientMenuItem.setName("showClientConfig");
         configureClientMenuItem.addActionListener(new java.awt.event.ActionListener() {
@@ -435,8 +425,20 @@
                 fireViewAction(Action.SWITCH_HISTORY_MODE);
             }
         });
+        editMenu.add(historyModeMenuItem);
 
-        editMenu.add(historyModeMenuItem);
+        JMenu viewMenu = new JMenu(localize(LocaleResources.MENU_VIEW));
+        mainMenuBar.add(viewMenu);
+        JMenuItem configureAgentMenuItem = new JMenuItem(localize(LocaleResources.MENU_VIEW_AGENTS));
+        configureAgentMenuItem.setName("showAgentConfig");
+        configureAgentMenuItem.addActionListener(new java.awt.event.ActionListener() {
+            @Override
+            public void actionPerformed(java.awt.event.ActionEvent e) {
+                fireViewAction(Action.SHOW_AGENT_CONFIG);
+            }
+        });
+        viewMenu.add(configureAgentMenuItem);
+
         JMenu helpMenu = new JMenu(localize(LocaleResources.MENU_HELP));
         helpMenu.getPopupMenu().setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1));
         mainMenuBar.add(helpMenu);
--- a/client/core/src/main/resources/com/redhat/thermostat/client/locale/strings.properties	Tue Sep 18 16:24:54 2012 +0200
+++ b/client/core/src/main/resources/com/redhat/thermostat/client/locale/strings.properties	Tue Sep 18 12:43:05 2012 -0400
@@ -17,9 +17,10 @@
 MENU_FILE = File
 MENU_FILE_EXIT = Exit
 MENU_EDIT = Edit
-MENU_EDIT_CONFIGURE_AGENT = Configure Agent...
 MENU_EDIT_CONFIGURE_CLIENT = Client Preferences...
 MENU_EDIT_ENABLE_HISTORY_MODE = Enable History Mode
+MENU_VIEW = View
+MENU_VIEW_AGENTS = View All Agents...
 MENU_HELP = Help
 MENU_HELP_ABOUT = About
 
@@ -143,9 +144,21 @@
 VM_CLASSES_CHART_LOADED_CLASSES_LABEL = Number of loaded classes
 VM_INFO_TAB_CLASSES = Classes
 
-CONFIGURE_AGENT_WINDOW_TITLE = Configure Agent Backends
-CONFIGURE_AGENT_AGENTS_LIST = Agents
-CONFIGURE_ENABLE_BACKENDS = Enable/Disable Backends
+AGENT_INFO_WINDOW_TITLE = Known Agents
+AGENT_INFO_AGENTS_LIST = Agents
+AGENT_INFO_AGENT_SECTION_TITLE = Agent Information
+AGENT_INFO_AGENT_NAME_LABEL = Name
+AGENT_INFO_AGENT_ID_LABEL = Id
+AGENT_INFO_AGENT_COMMAND_ADDRESS_LABEL = Command Channel Address
+AGENT_INFO_AGENT_START_TIME_LABEL = Start Time
+AGENT_INFO_AGENT_STOP_TIME_LABEL = Stop Time
+AGENT_INFO_AGENT_RUNNING = Running
+AGENT_INFO_BACKENDS_SECTION_TITLE = Backends
+AGENT_INFO_BACKEND_NAME_COLUMN = Name
+AGENT_INFO_BACKEND_STATUS_COLUMN = Status
+AGENT_INFO_BACKEND_STATUS_ACTIVE = Active
+AGENT_INFO_BACKEND_STATUS_INACTIVE = Inactive
+AGENT_INFO_BACKEND_DESCRIPTION_LABEL = Description
 
 CLIENT_PREFS_WINDOW_TITLE = Preferences
 CLIENT_PREFS_CONNECTION = Connection info
--- a/client/core/src/test/java/com/redhat/thermostat/client/internal/SwingViewFactoryTest.java	Tue Sep 18 16:24:54 2012 +0200
+++ b/client/core/src/test/java/com/redhat/thermostat/client/internal/SwingViewFactoryTest.java	Tue Sep 18 12:43:05 2012 -0400
@@ -45,7 +45,7 @@
 import org.junit.Test;
 
 import com.redhat.thermostat.client.internal.SwingViewFactory;
-import com.redhat.thermostat.client.ui.AgentConfigurationView;
+import com.redhat.thermostat.client.ui.AgentInformationDisplayView;
 import com.redhat.thermostat.client.ui.ClientConfigurationView;
 import com.redhat.thermostat.client.ui.HostCpuView;
 import com.redhat.thermostat.client.ui.HostMemoryView;
@@ -63,7 +63,7 @@
 
         List<Class<? extends View>> knownViewClasses = new ArrayList<>();
 
-        knownViewClasses.add(AgentConfigurationView.class);
+        knownViewClasses.add(AgentInformationDisplayView.class);
         knownViewClasses.add(ClientConfigurationView.class);
         knownViewClasses.add(HostCpuView.class);
         knownViewClasses.add(HostMemoryView.class);
--- a/client/core/src/test/java/com/redhat/thermostat/client/ui/AgentConfigurationControllerTest.java	Tue Sep 18 16:24:54 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,233 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.ui;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import com.redhat.thermostat.client.ui.AgentConfigurationView.ConfigurationAction;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.common.TimerFactory;
-import com.redhat.thermostat.common.appctx.ApplicationContext;
-import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
-
-public class AgentConfigurationControllerTest {
-
-    @Before
-    public void setUp() {
-        ApplicationContextUtil.resetApplicationContext();
-
-        /*
-         * Set up a mock timer factory that always executes actions synchronously on start();
-         */
-        TimerFactory tf = mock(TimerFactory.class);
-        Timer timer = mock(Timer.class);
-        final Runnable[] runnable = new Runnable[1];
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                runnable[0] = (Runnable) invocation.getArguments()[0];
-                return null;
-            }
-
-        }).when(timer).setAction(any(Runnable.class));
-
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                runnable[0].run();
-                return null;
-            }
-
-        }).when(timer).start();
-        when(tf.createTimer()).thenReturn(timer);
-        ApplicationContext.getInstance().setTimerFactory(tf);
-
-    }
-
-    @After
-    public void tearDown() {
-        ApplicationContextUtil.resetApplicationContext();
-    }
-
-    @Test
-    public void testAddingEnabledBackends() {
-        AgentConfigurationView view = mock(AgentConfigurationView.class);
-        AgentConfigurationModel model = mock(AgentConfigurationModel.class);
-        when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"}));
-        List<String> backends = Arrays.asList(new String[] {"backend1", "backend2"});
-        when(model.getBackends(any(String.class))).thenReturn(backends);
-        when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true);
-
-        Map<String,Boolean> expected = new HashMap<>();
-        expected.put("backend1", true);
-        expected.put("backend2", true);
-
-        AgentConfigurationController controller = new AgentConfigurationController(model, view);
-        controller.showView();
-        controller.hideView();
-
-        verify(view).addAgent("agent1");
-        verify(view).setBackendStatus(eq(expected));
-        verify(view).showDialog();
-        verify(view).hideDialog();
-    }
-
-    @Test
-    public void testAddingDisabledBackends() {
-        AgentConfigurationView view = mock(AgentConfigurationView.class);
-        AgentConfigurationModel model = mock(AgentConfigurationModel.class);
-        when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"}));
-        List<String> backends = Arrays.asList(new String[] {"backend1",});
-        when(model.getBackends(any(String.class))).thenReturn(backends);
-        when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(false);
-
-        Map<String,Boolean> expected = new HashMap<>();
-        expected.put("backend1", false);
-
-        AgentConfigurationController controller = new AgentConfigurationController(model, view);
-        controller.showView();
-        controller.hideView();
-
-        verify(view).addAgent("agent1");
-        verify(view).setBackendStatus(eq(expected));
-        verify(view).showDialog();
-        verify(view).hideDialog();
-    }
-
-    /**
-     * Verify that the accepting the changes signals the controller
-     */
-    @SuppressWarnings("unchecked") // All this ActionListener fluff
-    @Test
-    public void testViewEditedBackends() {
-        final ActionListener<ConfigurationAction>[] listeners = (ActionListener<ConfigurationAction>[]) new ActionListener<?>[1];
-        AgentConfigurationView view = mock(AgentConfigurationView.class);
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                listeners[0] = (ActionListener<ConfigurationAction>) invocation.getArguments()[0];
-                return null;
-            }
-        }).when(view).addActionListener(any(ActionListener.class));
-
-
-        AgentConfigurationModel model = mock(AgentConfigurationModel.class);
-        when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"}));
-        List<String> backends = Arrays.asList(new String[] {"backend1"});
-        when(model.getBackends(any(String.class))).thenReturn(backends);
-        when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true);
-
-        Map<String,Boolean> expected = new HashMap<>();
-        expected.put("backend1", true);
-
-        AgentConfigurationController controller = new AgentConfigurationController(model, view);
-        controller.showView();
-
-        listeners[0].actionPerformed(new ActionEvent<ConfigurationAction>(view, ConfigurationAction.CLOSE_ACCEPT));
-
-        controller.hideView();
-
-        InOrder inOrder = inOrder(view);
-
-        inOrder.verify(view).addAgent("agent1");
-        inOrder.verify(view).setBackendStatus(eq(expected));
-        inOrder.verify(view).getBackendStatus();
-
-        verify(model).saveConfiguration();
-    }
-
-    /**
-     * Verify that controller handles cancel properly
-     */
-    @SuppressWarnings("unchecked") // All this ActionListener fluff.
-    @Test
-    public void testViewCancelEditingBackends() {
-        final ActionListener<ConfigurationAction>[] listeners = (ActionListener<ConfigurationAction>[]) new ActionListener<?>[1];
-        AgentConfigurationView view = mock(AgentConfigurationView.class);
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                listeners[0] = (ActionListener<ConfigurationAction>) invocation.getArguments()[0];
-                return null;
-            }
-        }).when(view).addActionListener(any(ActionListener.class));
-
-
-        AgentConfigurationModel model = mock(AgentConfigurationModel.class);
-        when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"}));
-        List<String> backends = Arrays.asList(new String[] {"backend1"});
-        when(model.getBackends(any(String.class))).thenReturn(backends);
-        when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true);
-
-        Map<String,Boolean> expectedConfig = new HashMap<>();
-        expectedConfig.put("backend1", true);
-
-        AgentConfigurationController controller = new AgentConfigurationController(model, view);
-        controller.showView();
-
-        listeners[0].actionPerformed(new ActionEvent<ConfigurationAction>(view, ConfigurationAction.CLOSE_CANCEL));
-
-        controller.hideView();
-
-        verify(view).addAgent("agent1");
-        verify(view).setBackendStatus(eq(expectedConfig));
-        verify(view, never()).getBackendStatus();
-
-        verify(model, never()).saveConfiguration();
-    }
-}
--- a/client/core/src/test/java/com/redhat/thermostat/client/ui/AgentConfigurationFrameTest.java	Tue Sep 18 16:24:54 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.ui;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
-
-import org.fest.swing.edt.GuiActionRunner;
-import org.fest.swing.edt.GuiQuery;
-import org.fest.swing.edt.GuiTask;
-import org.fest.swing.fixture.FrameFixture;
-import org.fest.swing.fixture.JListFixture;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-
-@RunWith(CacioFESTRunner.class)
-public class AgentConfigurationFrameTest {
-
-    private AgentConfigurationFrame agentConfigFrame;
-    private FrameFixture fixture;
-    private ActionListener<AgentConfigurationView.ConfigurationAction> l;
-
-    @Before
-    public void setUp() {
-        agentConfigFrame = GuiActionRunner.execute(new GuiQuery<AgentConfigurationFrame>() {
-
-            @Override
-            protected AgentConfigurationFrame executeInEDT() throws Throwable {
-                 return new AgentConfigurationFrame();
-            }
-        });
-
-        @SuppressWarnings("unchecked")
-        ActionListener<AgentConfigurationView.ConfigurationAction> listener = mock(ActionListener.class);
-        l = listener;
-        agentConfigFrame.addActionListener(l);
-
-        fixture = new FrameFixture(agentConfigFrame);
-    }
-
-    @After
-    public void tearDown() {
-        GuiActionRunner.execute(new GuiTask() {
-            @Override
-            protected void executeInEDT() throws Throwable {
-                agentConfigFrame.hideDialog();
-            }
-        });
-
-        fixture.requireNotVisible();
-        agentConfigFrame.removeActionListener(l);
-
-        fixture.cleanUp();
-        fixture = null;
-    }
-
-    @Test
-    public void testAddingAgentWorks() {
-        fixture.show();
-        JListFixture list = fixture.list("agentList");
-        assertArrayEquals(new String[0], list.contents());
-
-        agentConfigFrame.addAgent("test-agent");
-
-        assertArrayEquals(new String[] {"test-agent"}, list.contents());
-    }
-
-    @Test
-    public void testSelectingAgentWorks() {
-        fixture.show();
-        agentConfigFrame.addAgent("testAgent");
-        JListFixture list = fixture.list("agentList");
-
-        list.selectItem("testAgent");
-
-        verify(l).actionPerformed(eq(new ActionEvent<>(agentConfigFrame, AgentConfigurationView.ConfigurationAction.SWITCH_AGENT)));
-    }
-
-    @Test
-    public void testRemovingAllAgentsWorks() {
-        fixture.show();
-        agentConfigFrame.addAgent("test-agent");
-        JListFixture list = fixture.list("agentList");
-
-        agentConfigFrame.clearAllAgents();
-
-        assertArrayEquals(new String[0], list.contents());
-    }
-
-    @Test
-    public void testWindowClose() {
-        fixture.show();
-
-        fixture.close();
-
-        verify(l).actionPerformed(eq(new ActionEvent<>(agentConfigFrame, AgentConfigurationView.ConfigurationAction.CLOSE_CANCEL)));
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/test/java/com/redhat/thermostat/client/ui/AgentInformationDisplayControllerTest.java	Tue Sep 18 12:43:05 2012 -0400
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.client.ui.AgentInformationDisplayView.ConfigurationAction;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.model.AgentInformation;
+import com.redhat.thermostat.common.model.BackendInformation;
+
+public class AgentInformationDisplayControllerTest {
+
+    private AgentInformation agentInfo1;
+    private BackendInformation backendInfo1;
+
+    @Before
+    public void setUp() {
+        setUpAgentAndBackendInformation();
+    }
+
+    private void setUpAgentAndBackendInformation() {
+        agentInfo1 = new AgentInformation();
+        agentInfo1.setAgentId("agent-1");
+
+        backendInfo1 = new BackendInformation();
+        backendInfo1.setName("backend-1");
+        backendInfo1.setDescription("backend-description1");
+    }
+
+    @Test
+    public void testShowView() {
+        AgentInformationDisplayView view = mock(AgentInformationDisplayView.class);
+        AgentInformationDisplayModel model = mock(AgentInformationDisplayModel.class);
+
+        when(model.getAgents()).thenReturn(Arrays.asList(agentInfo1));
+        when(model.getAgentInfo(agentInfo1.getAgentId())).thenReturn(agentInfo1);
+
+        AgentInformationDisplayController controller = new AgentInformationDisplayController(model, view);
+        controller.showView();
+
+        verify(view).showDialog();
+    }
+
+    @Test
+    public void testHideView() {
+        AgentInformationDisplayView view = mock(AgentInformationDisplayView.class);
+        AgentInformationDisplayModel model = mock(AgentInformationDisplayModel.class);
+
+        AgentInformationDisplayController controller = new AgentInformationDisplayController(model, view);
+        controller.hideView();
+
+        verify(view).hideDialog();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testHidViewWhenViewFiresClose() {
+        AgentInformationDisplayView view = mock(AgentInformationDisplayView.class);
+        AgentInformationDisplayModel model = mock(AgentInformationDisplayModel.class);
+
+        @SuppressWarnings("rawtypes")
+        ArgumentCaptor<ActionListener> captor = ArgumentCaptor.forClass(ActionListener.class);
+
+        @SuppressWarnings("unused")
+        AgentInformationDisplayController controller = new AgentInformationDisplayController(model, view);
+
+        verify(view).addConfigurationListener(captor.capture());
+        ActionListener<ConfigurationAction> listener = captor.getValue();
+        listener.actionPerformed(new ActionEvent<ConfigurationAction>(view, ConfigurationAction.CLOSE));
+
+        verify(view).hideDialog();
+    }
+
+    @Test
+    public void testAddAgentAndBackendsOnInit() {
+        AgentInformationDisplayView view = mock(AgentInformationDisplayView.class);
+        AgentInformationDisplayModel model = mock(AgentInformationDisplayModel.class);
+
+        when(model.getAgents()).thenReturn(Arrays.asList(agentInfo1));
+        when(model.getAgentInfo(agentInfo1.getAgentId())).thenReturn(agentInfo1);
+        when(model.getBackends(agentInfo1.getAgentId())).thenReturn(Arrays.asList(backendInfo1));
+
+        Map<String, String> expected = new HashMap<>();
+        expected.put(backendInfo1.getName(), backendInfo1.isActive() ? "Active" : "Inactive");
+
+        AgentInformationDisplayController controller = new AgentInformationDisplayController(model, view);
+        controller.showView();
+
+        verify(view).addAgent(agentInfo1.getAgentId());
+
+        verify(view).setSelectedAgentName(agentInfo1.getAgentId());
+        verify(view).setSelectedAgentId(agentInfo1.getAgentId());
+        verify(view).setSelectedAgentCommandAddress(agentInfo1.getConfigListenAddress());
+        verify(view).setSelectedAgentStartTime(isA(String.class));
+        verify(view).setSelectedAgentStopTime(isA(String.class));
+
+        verify(view).setSelectedAgentBackendStatus(eq(expected));
+        verify(view).showDialog();
+    }
+
+    @Test
+    public void testUpdateAgentInformationOnAgentSelection() {
+        AgentInformationDisplayView view = mock(AgentInformationDisplayView.class);
+        AgentInformationDisplayModel model = mock(AgentInformationDisplayModel.class);
+
+        when(model.getAgents()).thenReturn(Arrays.asList(agentInfo1));
+        when(model.getAgentInfo(agentInfo1.getAgentId())).thenReturn(agentInfo1);
+        when(model.getBackends(agentInfo1.getAgentId())).thenReturn(Arrays.asList(backendInfo1));
+
+        when(view.getSelectedAgent()).thenReturn(agentInfo1.getAgentId());
+
+        @SuppressWarnings("rawtypes")
+        ArgumentCaptor<ActionListener> captor = ArgumentCaptor.forClass(ActionListener.class);
+
+        @SuppressWarnings("unused")
+        AgentInformationDisplayController controller = new AgentInformationDisplayController(model, view);
+
+        verify(view).addConfigurationListener(captor.capture());
+        ActionListener<ConfigurationAction> listener = captor.getValue();
+        listener.actionPerformed(new ActionEvent<ConfigurationAction>(view, ConfigurationAction.SWITCH_AGENT));
+
+        verify(view, never()).addAgent(agentInfo1.getAgentId());
+
+        verify(view).setSelectedAgentName(agentInfo1.getAgentId());
+        verify(view).setSelectedAgentId(agentInfo1.getAgentId());
+        verify(view).setSelectedAgentCommandAddress(agentInfo1.getConfigListenAddress());
+        verify(view).setSelectedAgentStartTime(isA(String.class));
+        verify(view).setSelectedAgentStopTime(isA(String.class));
+
+        Map<String, String> expected = new HashMap<>();
+        expected.put(backendInfo1.getName(), backendInfo1.isActive() ? "Active" : "Inactive");
+
+        verify(view).setSelectedAgentBackendStatus(eq(expected));
+    }
+
+    @Test
+    public void testUpdateDescriptionOnRequest() {
+        AgentInformationDisplayModel model = mock(AgentInformationDisplayModel.class);
+        AgentInformationDisplayView view = mock(AgentInformationDisplayView.class);
+
+        when(model.getAgents()).thenReturn(Arrays.asList(agentInfo1));
+        when(model.getAgentInfo(agentInfo1.getAgentId())).thenReturn(agentInfo1);
+        when(model.getBackends(agentInfo1.getAgentId())).thenReturn(Arrays.asList(backendInfo1));
+
+        when(view.getSelectedAgent()).thenReturn(agentInfo1.getAgentId());
+
+        Map<String, String> expected = new HashMap<>();
+        expected.put(backendInfo1.getName(), backendInfo1.isActive() ? "Active" : "Inactive");
+
+        ArgumentCaptor<ActionListener> listenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        AgentInformationDisplayController controller = new AgentInformationDisplayController(model, view);
+
+        verify(view).addConfigurationListener(listenerCaptor.capture());
+
+        ActionEvent<ConfigurationAction> showBackendDescription = new ActionEvent<>(view, ConfigurationAction.SHOW_BACKEND_DESCRIPTION);
+        showBackendDescription.setPayload(backendInfo1.getName());
+
+        listenerCaptor.getValue().actionPerformed(showBackendDescription);
+
+        verify(view).setSelectedAgentBackendDescription(backendInfo1.getDescription());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/test/java/com/redhat/thermostat/client/ui/AgentInformationDisplayFrameTest.java	Tue Sep 18 12:43:05 2012 -0400
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiQuery;
+import org.fest.swing.edt.GuiTask;
+import org.fest.swing.fixture.FrameFixture;
+import org.fest.swing.fixture.JListFixture;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import com.redhat.thermostat.client.ui.AgentInformationDisplayView.ConfigurationAction;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+
+@RunWith(CacioFESTRunner.class)
+public class AgentInformationDisplayFrameTest {
+
+    private AgentInformationDisplayFrame agentConfigFrame;
+    private FrameFixture fixture;
+    private ActionListener<AgentInformationDisplayView.ConfigurationAction> l;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
+    @Before
+    public void setUp() {
+        agentConfigFrame = GuiActionRunner.execute(new GuiQuery<AgentInformationDisplayFrame>() {
+
+            @Override
+            protected AgentInformationDisplayFrame executeInEDT() throws Throwable {
+                return new AgentInformationDisplayFrame();
+            }
+        });
+
+        @SuppressWarnings("unchecked")
+        ActionListener<AgentInformationDisplayView.ConfigurationAction> listener = mock(ActionListener.class);
+        l = listener;
+        agentConfigFrame.addConfigurationListener(l);
+
+        fixture = new FrameFixture(agentConfigFrame.getFrame());
+    }
+
+    @After
+    public void tearDown() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                agentConfigFrame.hideDialog();
+            }
+        });
+
+        fixture.requireNotVisible();
+        agentConfigFrame.removeConfigurationListener(l);
+
+        fixture.cleanUp();
+        fixture = null;
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testWindowClose() {
+        fixture.show();
+
+        fixture.close();
+
+        verify(l).actionPerformed(eq(new ActionEvent<>(agentConfigFrame, AgentInformationDisplayView.ConfigurationAction.CLOSE)));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testClickOnCloseButton() {
+        fixture.show();
+
+        fixture.button("close").click();
+
+        fixture.robot.waitForIdle();
+
+        verify(l).actionPerformed(eq(new ActionEvent<>(agentConfigFrame, AgentInformationDisplayView.ConfigurationAction.CLOSE)));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testAddingAgentWorks() {
+        fixture.show();
+        JListFixture list = fixture.list("agentList");
+        assertArrayEquals(new String[0], list.contents());
+
+        agentConfigFrame.addAgent("test-agent");
+
+        assertArrayEquals(new String[] { "test-agent" }, list.contents());
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testSelectingAgentWorks() {
+        fixture.show();
+        agentConfigFrame.addAgent("testAgent");
+        JListFixture list = fixture.list("agentList");
+
+        list.selectItem("testAgent");
+
+        verify(l, atLeast(1)).actionPerformed(eq(new ActionEvent<>(agentConfigFrame, AgentInformationDisplayView.ConfigurationAction.SWITCH_AGENT)));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testFirstAddedAgentIsAutomaticallySelected() {
+        fixture.show();
+        agentConfigFrame.addAgent("testAgent");
+
+        fixture.robot.waitForIdle();
+
+        verify(l).actionPerformed(eq(new ActionEvent<>(agentConfigFrame, AgentInformationDisplayView.ConfigurationAction.SWITCH_AGENT)));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testRemovingAllAgentsWorks() {
+        fixture.show();
+        agentConfigFrame.addAgent("test-agent");
+        JListFixture list = fixture.list("agentList");
+
+        agentConfigFrame.clearAllAgents();
+
+        assertArrayEquals(new String[0], list.contents());
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testUpdatingAgentInformationWorks() {
+
+        final String AGENT_NAME = "the-agent-name";
+        final String AGENT_ID = "the-agent-id";
+        final String COMMAND_ADDRESS = "agent-command-channel-address";
+        final String START_TIME = "some-start-time";
+        final String STOP_TIME = "a-certain-stop-time";
+
+        agentConfigFrame.setSelectedAgentName(AGENT_NAME);
+        agentConfigFrame.setSelectedAgentId(AGENT_ID);
+        agentConfigFrame.setSelectedAgentCommandAddress(COMMAND_ADDRESS);
+        agentConfigFrame.setSelectedAgentStartTime(START_TIME);
+        agentConfigFrame.setSelectedAgentStopTime(STOP_TIME);
+
+        fixture.show();
+
+        assertEquals(AGENT_NAME, fixture.textBox("agentName").text());
+        assertEquals(AGENT_ID, fixture.textBox("agentId").text());
+        assertEquals(COMMAND_ADDRESS, fixture.textBox("commandAddress").text());
+        assertEquals(START_TIME, fixture.textBox("startTime").text());
+        assertEquals(STOP_TIME, fixture.textBox("stopTime").text());
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testBackendDescriptionIsQueriedAndDisplayed() {
+        final String BACKEND_NAME = "foo";
+        final String BACKEND_STATUS = "bar";
+        final String BACKEND_DESCRIPTION = "baz";
+
+        Map<String, String> statusMap = new HashMap<>();
+        statusMap.put(BACKEND_NAME, BACKEND_STATUS);
+
+        fixture.show();
+
+        agentConfigFrame.setSelectedAgentBackendStatus(statusMap);
+
+        assertEquals(1, fixture.table("backends").rowCount());
+
+        String[] rowContents = fixture.table("backends").contents()[0];
+        assertArrayEquals(new String[] { BACKEND_NAME, BACKEND_STATUS }, rowContents);
+
+        fixture.table("backends").selectRows(0);
+
+        verify(l).actionPerformed(new ActionEvent<ConfigurationAction>(agentConfigFrame, ConfigurationAction.SHOW_BACKEND_DESCRIPTION));
+
+        agentConfigFrame.setSelectedAgentBackendDescription(BACKEND_DESCRIPTION);
+
+        assertEquals(BACKEND_DESCRIPTION, fixture.textBox("backendDescription").text());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/test/java/com/redhat/thermostat/client/ui/AgentInformationDisplayModelTest.java	Tue Sep 18 12:43:05 2012 -0400
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
+import com.redhat.thermostat.common.dao.AgentInfoDAO;
+import com.redhat.thermostat.common.dao.BackendInfoDAO;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.model.AgentInformation;
+
+public class AgentInformationDisplayModelTest {
+
+    private AgentInformation agentInfo1 = new AgentInformation();
+    private AgentInformation agentInfo2 = new AgentInformation();
+
+    private DAOFactory daoFactory = mock(DAOFactory.class);
+    private AgentInfoDAO agentInfoDao = mock(AgentInfoDAO.class);
+    private BackendInfoDAO backendInfoDao = mock(BackendInfoDAO.class);
+
+    @Before
+    public void setUp() {
+        ApplicationContextUtil.resetApplicationContext();
+
+        when(daoFactory.getAgentInfoDAO()).thenReturn(agentInfoDao);
+        when(daoFactory.getBackendInfoDAO()).thenReturn(backendInfoDao);
+
+        agentInfo1.setAgentId("agent1-id");
+        agentInfo1.setStartTime(0);
+        agentInfo1.setStopTime(1);
+        agentInfo1.setConfigListenAddress("config-address:port");
+        agentInfo1.setAlive(false);
+
+        agentInfo2.setAgentId("agent2-id");
+
+        when(agentInfoDao.getAllAgentInformation()).thenReturn(Arrays.asList(agentInfo1));
+
+        ApplicationContext.getInstance().setDAOFactory(daoFactory);
+    }
+
+    @After
+    public void tearDown() {
+        ApplicationContextUtil.resetApplicationContext();
+    }
+
+    @Test
+    public void testModelInitializesItself() {
+        AgentInformationDisplayModel model = new AgentInformationDisplayModel();
+
+        AgentInformation agentInfoFromModel = model.getAgentInfo(agentInfo1.getAgentId());
+
+        assertEquals(agentInfo1, agentInfoFromModel);
+    }
+
+    @Test
+    public void testGetUnknownAgentInformation() {
+        AgentInformationDisplayModel model = new AgentInformationDisplayModel();
+
+        AgentInformation agentInfoFromModel = model.getAgentInfo("some unknown agent id");
+
+        assertEquals(null, agentInfoFromModel);
+    }
+
+    @Test
+    public void testChangesOnlyVisibleAfterRefresh() {
+        AgentInformationDisplayModel model = new AgentInformationDisplayModel();
+        AgentInformation agentInfoFromModel;
+
+        agentInfoFromModel = model.getAgentInfo(agentInfo1.getAgentId());
+
+        assertEquals(agentInfo1, agentInfoFromModel);
+
+        when(agentInfoDao.getAllAgentInformation()).thenReturn(Arrays.asList(agentInfo2));
+
+        agentInfoFromModel = model.getAgentInfo(agentInfo1.getAgentId());
+
+        assertEquals(agentInfo1, agentInfoFromModel);
+
+        model.refresh();
+
+        agentInfoFromModel = model.getAgentInfo(agentInfo1.getAgentId());
+
+        assertEquals(null, agentInfoFromModel);
+
+    }
+}