changeset 508:fb1701524556

Clean up the host overview tab Layout swing components explicitly. This allows the view to behave more like other views that we have. It also makes it easier to see if code violates the EDT requirements. Also allows the columns of the network table to be reordered without resetting the entire table. Add some tests for the controller too. Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-July/002517.html
author Omair Majid <omajid@redhat.com>
date Mon, 30 Jul 2012 15:19:14 -0400
parents 1974f63429ba
children 7bab8e5ee4c4
files client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewController.java client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewPanel.java client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewView.java client/core/src/main/java/com/redhat/thermostat/client/ui/LabelField.java client/core/src/main/java/com/redhat/thermostat/client/ui/SectionHeader.java client/core/src/main/java/com/redhat/thermostat/client/ui/ValueField.java client/core/src/test/java/com/redhat/thermostat/client/ui/HostOverviewControllerTest.java
diffstat 7 files changed, 489 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewController.java	Mon Jul 30 13:01:39 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewController.java	Mon Jul 30 15:19:14 2012 -0400
@@ -41,13 +41,9 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Vector;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import javax.swing.SwingWorker;
-
 import com.redhat.thermostat.client.locale.LocaleResources;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
@@ -74,6 +70,7 @@
     private final NetworkInterfaceInfoDAO networkInfoDAO;
 
     private final Timer backgroundUpdateTimer;
+    private final List<String> knownNetworkIfaces = new ArrayList<>();
 
     private final HostOverviewView view;
 
@@ -101,7 +98,31 @@
                 view.setCpuCount(String.valueOf(hostInfo.getCpuCount()));
                 view.setTotalMemory(String.valueOf(hostInfo.getTotalMemory()));
 
-                doNetworkTableUpdateAsync();
+                List<NetworkInterfaceInfo> networkInfo = networkInfoDAO.getNetworkInterfaces(ref);
+
+                boolean firstRun = knownNetworkIfaces.isEmpty();
+                if (firstRun) {
+                    List<Object[]> data = new ArrayList<Object[]>();
+                    for (NetworkInterfaceInfo info: networkInfo) {
+                        String ifaceName = info.getInterfaceName();
+                        String ipv4 = info.getIp4Addr();
+                        String ipv6 = info.getIp6Addr();
+                        data.add(new String[] { ifaceName, ipv4, ipv6 });
+                        knownNetworkIfaces.add(ifaceName);
+                    }
+                    view.setInitialNetworkTableData(data.toArray(new Object[0][0]));
+                } else {
+                    for (NetworkInterfaceInfo info: networkInfo) {
+                        String ifaceName = info.getInterfaceName();
+                        String ipv4 = info.getIp4Addr();
+                        String ipv6 = info.getIp6Addr();
+                        int index = knownNetworkIfaces.indexOf(ifaceName);
+                        int row = index;
+                        view.updateNetworkTableData(index, 0, ifaceName);
+                        view.updateNetworkTableData(row, 1, ipv4);
+                        view.updateNetworkTableData(row, 2, ipv6);
+                    }
+                }
             }
         });
         backgroundUpdateTimer.setSchedulingType(SchedulingType.FIXED_RATE);
@@ -130,42 +151,6 @@
         });
     }
 
-    private void doNetworkTableUpdateAsync() {
-        new NetworkTableModelUpdater().execute();
-    }
-
-    private class NetworkTableModelUpdater extends SwingWorker<List<NetworkInterfaceInfo>, Void> {
-
-        @Override
-        protected List<NetworkInterfaceInfo> doInBackground() throws Exception {
-            return networkInfoDAO.getNetworkInterfaces(ref);
-        }
-
-        @Override
-        protected void done() {
-            List<Object[]> data = new ArrayList<Object[]>();
-
-            List<NetworkInterfaceInfo> networkInfo;
-            try {
-                networkInfo = get();
-                for (NetworkInterfaceInfo info: networkInfo) {
-                    String ifaceName = info.getInterfaceName();
-                    String ipv4 = info.getIp4Addr();
-                    String ipv6 = info.getIp6Addr();
-                    data.add(new String[] { ifaceName, ipv4, ipv6 });
-                }
-                view.setNetworkTableData(data.toArray(new Object[0][0]));
-
-            } catch (InterruptedException ie) {
-                logger.log(Level.WARNING, "interrupted while updating network info", ie);
-                // preserve interrupted flag
-                Thread.currentThread().interrupt();
-            } catch (ExecutionException ee) {
-                logger.log(Level.WARNING, "error updating network info", ee);
-            }
-        }
-    }
-
     private void start() {
         backgroundUpdateTimer.start();
     }
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewPanel.java	Mon Jul 30 13:01:39 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewPanel.java	Mon Jul 30 15:19:14 2012 -0400
@@ -38,36 +38,40 @@
 
 import static com.redhat.thermostat.client.locale.Translate.localize;
 
-import java.awt.BorderLayout;
 import java.awt.Component;
-import java.util.ArrayList;
-import java.util.List;
 
 import javax.swing.JPanel;
 import javax.swing.JTable;
+import javax.swing.SwingUtilities;
 import javax.swing.table.DefaultTableModel;
+import javax.swing.table.JTableHeader;
 
-import com.redhat.thermostat.client.internal.ChangeableText;
 import com.redhat.thermostat.client.locale.LocaleResources;
-import com.redhat.thermostat.client.ui.SimpleTable.Key;
-import com.redhat.thermostat.client.ui.SimpleTable.Section;
-import com.redhat.thermostat.client.ui.SimpleTable.TableEntry;
-import com.redhat.thermostat.client.ui.SimpleTable.Value;
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.BasicView;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import java.awt.BorderLayout;
 
 public class HostOverviewPanel extends HostOverviewView implements SwingComponent {
 
     private JPanel visiblePanel;
-    
-    private final ChangeableText hostname = new ChangeableText("");
-    private final ChangeableText cpuModel = new ChangeableText("");
-    private final ChangeableText cpuCount = new ChangeableText("");
-    private final ChangeableText totalMemory = new ChangeableText("");
-    private final ChangeableText osName = new ChangeableText("");
-    private final ChangeableText osKernel = new ChangeableText("");
+
+    private final ValueField hostname = new ValueField("${hostname}");
+    private final ValueField cpuModel = new ValueField("${cpu-model}");
+    private final ValueField cpuCount = new ValueField("${cpu-count}");
+    private final ValueField totalMemory = new ValueField("${total-memory}");
+    private final ValueField osName = new ValueField("${os-name}");
+    private final ValueField osKernel = new ValueField("${os-kernel}");
 
-    private final DefaultTableModel networkTableModel = new DefaultTableModel();
+    private final DefaultTableModel networkTableModel = new DefaultTableModel() {
+        @Override
+        public boolean isCellEditable(int row, int column) {
+            return false;
+        }
+    };
+
     private Object[] networkTableColumns;
     private Object[][] networkTableData;
 
@@ -99,45 +103,95 @@
     }
 
     @Override
-    public void setHostName(String newHostName) {
-        hostname.setText(newHostName);
+    public void setHostName(final String newHostName) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                hostname.setText(newHostName);
+            }
+        });
     }
 
     @Override
-    public void setCpuModel(String newCpuModel) {
-        cpuModel.setText(newCpuModel);
+    public void setCpuModel(final String newCpuModel) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                cpuModel.setText(newCpuModel);
+            }
+        });
     }
 
     @Override
-    public void setCpuCount(String newCpuCount) {
-        cpuCount.setText(newCpuCount);
+    public void setCpuCount(final String newCpuCount) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                cpuCount.setText(newCpuCount);
+            }
+        });
     }
 
     @Override
-    public void setTotalMemory(String newTotalMemory) {
-        totalMemory.setText(newTotalMemory);
+    public void setTotalMemory(final String newTotalMemory) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                totalMemory.setText(newTotalMemory);
+            }
+        });
     }
 
     @Override
-    public void setOsName(String newOsName) {
-        osName.setText(newOsName);
+    public void setOsName(final String newOsName) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                osName.setText(newOsName);
+            }
+        });
     }
 
     @Override
-    public void setOsKernel(String newOsKernel) {
-        osKernel.setText(newOsKernel);
+    public void setOsKernel(final String newOsKernel) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                osKernel.setText(newOsKernel);
+            }
+        });
     }
 
     @Override
-    public void setNetworkTableColumns(Object[] columns) {
+    public void setNetworkTableColumns(final Object[] columns) {
         this.networkTableColumns = columns;
-        networkTableModel.setDataVector(this.networkTableData, this.networkTableColumns);
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                networkTableModel.setColumnIdentifiers(networkTableColumns);
+            }
+        });
     }
 
     @Override
-    public void setNetworkTableData(Object[][] data) {
+    public void setInitialNetworkTableData(final Object[][] data) {
         this.networkTableData = data;
-        networkTableModel.setDataVector(this.networkTableData, this.networkTableColumns);
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                networkTableModel.setDataVector(networkTableData, networkTableColumns);
+            }
+        });
+    }
+
+    @Override
+    public void updateNetworkTableData(final int row, final int column, final String data) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                networkTableModel.setValueAt(data, row, column);
+            }
+        });
     }
 
     @Override
@@ -147,53 +201,108 @@
 
     private void initializePanel() {
         visiblePanel = new JPanel();
-        TableEntry entry;
-        List<Section> allSections = new ArrayList<Section>();
+        SectionHeader overviewSection = new SectionHeader(localize(LocaleResources.HOST_OVERVIEW_SECTION_BASICS));
+        LabelField hostnameLabel = new LabelField(localize(LocaleResources.HOST_INFO_HOSTNAME));
+        SectionHeader hardwareSection = new SectionHeader(localize(LocaleResources.HOST_OVERVIEW_SECTION_HARDWARE));
+        LabelField cpuModelLabel = new LabelField(localize(LocaleResources.HOST_INFO_CPU_MODEL));
+        LabelField cpuCountLabel = new LabelField(localize(LocaleResources.HOST_INFO_CPU_COUNT));
+        LabelField memoryTotalLabel = new LabelField(localize(LocaleResources.HOST_INFO_MEMORY_TOTAL));
+        LabelField networkLabel = new LabelField(localize(LocaleResources.HOST_INFO_NETWORK));
+        SectionHeader softwareSection = new SectionHeader(localize(LocaleResources.HOST_OVERVIEW_SECTION_SOFTWARE));
+        LabelField osNameLabel = new LabelField(localize(LocaleResources.HOST_INFO_OS_NAME));
+        LabelField osKernelLabel = new LabelField(localize(LocaleResources.HOST_INFO_OS_KERNEL));
 
-        Section basics = new Section(localize(LocaleResources.HOST_OVERVIEW_SECTION_BASICS));
-        allSections.add(basics);
-
-        entry = new TableEntry(localize(LocaleResources.HOST_INFO_HOSTNAME), hostname);
-        basics.add(entry);
+        JPanel panel = new JPanel();
 
-        Section hardware = new Section(localize(LocaleResources.HOST_OVERVIEW_SECTION_HARDWARE));
-        allSections.add(hardware);
+        GroupLayout gl_visiblePanel = new GroupLayout(visiblePanel);
+        gl_visiblePanel.setHorizontalGroup(
+            gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                .addGroup(gl_visiblePanel.createSequentialGroup()
+                    .addContainerGap()
+                    .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                        .addComponent(hardwareSection, GroupLayout.DEFAULT_SIZE, 620, Short.MAX_VALUE)
+                        .addComponent(overviewSection, GroupLayout.DEFAULT_SIZE, 620, Short.MAX_VALUE)
+                        .addGroup(gl_visiblePanel.createSequentialGroup()
+                            .addGroup(gl_visiblePanel.createParallelGroup(Alignment.TRAILING, false)
+                                .addGroup(gl_visiblePanel.createSequentialGroup()
+                                    .addGap(12)
+                                    .addComponent(hostnameLabel, GroupLayout.DEFAULT_SIZE, 134, Short.MAX_VALUE))
+                                .addComponent(cpuCountLabel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(cpuModelLabel, GroupLayout.DEFAULT_SIZE, 134, Short.MAX_VALUE)
+                                .addComponent(memoryTotalLabel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addGroup(gl_visiblePanel.createSequentialGroup()
+                                    .addGap(12)
+                                    .addComponent(networkLabel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+                            .addPreferredGap(ComponentPlacement.RELATED)
+                            .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                                .addComponent(panel, GroupLayout.DEFAULT_SIZE, 462, Short.MAX_VALUE)
+                                .addComponent(cpuCount, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(cpuModel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(hostname, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(totalMemory, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+                        .addComponent(softwareSection, GroupLayout.DEFAULT_SIZE, 620, Short.MAX_VALUE)
+                        .addGroup(gl_visiblePanel.createSequentialGroup()
+                            .addGap(12)
+                            .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING, false)
+                                .addComponent(osKernelLabel, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(osNameLabel, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 129, Short.MAX_VALUE))
+                            .addPreferredGap(ComponentPlacement.RELATED)
+                            .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                                .addComponent(osKernel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(osName, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+                    .addContainerGap())
+        );
+        gl_visiblePanel.setVerticalGroup(
+            gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                .addGroup(gl_visiblePanel.createSequentialGroup()
+                    .addContainerGap()
+                    .addComponent(overviewSection, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING, false)
+                        .addComponent(hostname, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
+                        .addComponent(hostnameLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                    .addPreferredGap(ComponentPlacement.UNRELATED)
+                    .addComponent(hardwareSection, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING, false)
+                        .addComponent(cpuModelLabel, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
+                        .addComponent(cpuModel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING, false)
+                        .addComponent(cpuCountLabel, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
+                        .addComponent(cpuCount, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                        .addComponent(memoryTotalLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addComponent(totalMemory, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                        .addComponent(networkLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addComponent(panel, GroupLayout.PREFERRED_SIZE, 109, GroupLayout.PREFERRED_SIZE))
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(softwareSection, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                        .addComponent(osNameLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addComponent(osName, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_visiblePanel.createParallelGroup(Alignment.LEADING)
+                        .addComponent(osKernelLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addComponent(osKernel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                    .addGap(128))
+        );
 
-        entry = new TableEntry(localize(LocaleResources.HOST_INFO_CPU_MODEL), cpuModel);
-        hardware.add(entry);
-        entry = new TableEntry(localize(LocaleResources.HOST_INFO_CPU_COUNT), cpuCount);
-        hardware.add(entry);
-        entry = new TableEntry(localize(LocaleResources.HOST_INFO_MEMORY_TOTAL), totalMemory);
-        hardware.add(entry);
+        panel.setLayout(new BorderLayout(0, 0));
 
         JTable networkTable = new JTable(networkTableModel);
-
-        JPanel networkPanel = new JPanel(new BorderLayout());
-        networkPanel.add(networkTable.getTableHeader(), BorderLayout.PAGE_START);
-        networkPanel.add(networkTable, BorderLayout.CENTER);
-
-        Key key = new Key(localize(LocaleResources.HOST_INFO_NETWORK));
-        hardware.add(new TableEntry(key, new Value(networkPanel)));
-
-        Section software = new Section(localize(LocaleResources.HOST_OVERVIEW_SECTION_SOFTWARE));
-        allSections.add(software);
-
-        entry = new TableEntry(localize(LocaleResources.HOST_INFO_OS_NAME), osName);
-        software.add(entry);
-        entry = new TableEntry(localize(LocaleResources.HOST_INFO_OS_KERNEL), osKernel);
-        software.add(entry);
-
-        SimpleTable simpleTable = new SimpleTable();
-        JPanel table = simpleTable.createTable(allSections);
-        table.setBorder(Components.smallBorder());
-
-        visiblePanel.setLayout(new BorderLayout());
-        visiblePanel.add(table, BorderLayout.CENTER);
+        panel.add(networkTable);
+        JTableHeader header = networkTable.getTableHeader();
+        panel.add(header, BorderLayout.PAGE_START);
+        visiblePanel.setLayout(gl_visiblePanel);
     }
 
     @Override
     public BasicView getView() {
         return this;
     }
-
 }
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewView.java	Mon Jul 30 13:01:39 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/HostOverviewView.java	Mon Jul 30 15:19:14 2012 -0400
@@ -54,6 +54,7 @@
 
     public abstract void setNetworkTableColumns(Object[] columns);
 
-    public abstract void setNetworkTableData(Object[][] table);
+    public abstract void setInitialNetworkTableData(Object[][] table);
 
+    public abstract void updateNetworkTableData(int row, int column, String data);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/LabelField.java	Mon Jul 30 15:19:14 2012 -0400
@@ -0,0 +1,48 @@
+/*
+ * 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 javax.swing.JLabel;
+import javax.swing.SwingConstants;
+
+public class LabelField extends JLabel {
+
+    public LabelField(String text) {
+        super(text);
+        setHorizontalAlignment(SwingConstants.TRAILING);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/SectionHeader.java	Mon Jul 30 15:19:14 2012 -0400
@@ -0,0 +1,48 @@
+/*
+ * 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 javax.swing.JLabel;
+import javax.swing.SwingConstants;
+
+public class SectionHeader extends JLabel {
+
+    public SectionHeader(String text) {
+        super(HtmlTextBuilder.boldHtml(text));
+        setHorizontalAlignment(SwingConstants.LEADING);
+    }
+}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/ValueField.java	Mon Jul 30 13:01:39 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/ValueField.java	Mon Jul 30 15:19:14 2012 -0400
@@ -48,6 +48,7 @@
     public ValueField(String text) {
         setText(text);
         setBorder(null);
+        setOpaque(false);
         setBackground(UIManager.getColor("Label.background"));
         setForeground(UIManager.getColor("Label.foreground"));
         setFont(UIManager.getFont("Label.font"));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/test/java/com/redhat/thermostat/client/ui/HostOverviewControllerTest.java	Mon Jul 30 15:19:14 2012 -0400
@@ -0,0 +1,185 @@
+/*
+ * 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.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNotNull;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.BasicView.Action;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.ViewFactory;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.HostInfoDAO;
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
+import com.redhat.thermostat.common.model.HostInfo;
+import com.redhat.thermostat.common.model.NetworkInterfaceInfo;
+
+public class HostOverviewControllerTest {
+
+    private static final String HOST_NAME = "host-name";
+    private static final String OS_NAME = "some os";
+    private static final String KERNEL_NAME = "korn";
+    private static final int CPU_COUNT = 99;
+    private static final String CPU_MODEL = "cpu-model";
+    private static final long TOTAL_MEMORY = 99+99;
+    private static final String NETWORK_INTERFACE = "iface0";
+    private static final String IPV4_ADDR = "0xcafef00d";
+    private static final String IPV6_ADDR = "HOME_SWEET_HOME";
+
+    private Timer timer;
+    private Runnable timerAction;
+    private HostOverviewView view;
+    private ActionListener<HostOverviewView.Action> listener;
+
+    @Before
+    public void setUp() {
+        ApplicationContextUtil.resetApplicationContext();
+
+        // Setup timer
+        timer = mock(Timer.class);
+        ArgumentCaptor<Runnable> timerActionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(timerActionCaptor.capture());
+
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+        ApplicationContext.getInstance().setTimerFactory(timerFactory);
+
+        // Setup DAOs
+        HostInfo hostInfo = new HostInfo(HOST_NAME, OS_NAME, KERNEL_NAME, CPU_MODEL, CPU_COUNT, TOTAL_MEMORY);
+
+        List<NetworkInterfaceInfo> networkInfo = new ArrayList<NetworkInterfaceInfo>();
+        NetworkInterfaceInfo ifaceInfo = new NetworkInterfaceInfo(NETWORK_INTERFACE);
+        ifaceInfo.setIp4Addr(IPV4_ADDR);
+        ifaceInfo.setIp6Addr(IPV6_ADDR);
+        networkInfo.add(ifaceInfo);
+
+        HostRef ref = mock(HostRef.class);
+
+        DAOFactory daoFactory = mock(DAOFactory.class);
+
+        HostInfoDAO hostInfoDao = mock(HostInfoDAO.class);
+        when(hostInfoDao.getHostInfo(any(HostRef.class))).thenReturn(hostInfo);
+
+        NetworkInterfaceInfoDAO networkInfoDao = mock(NetworkInterfaceInfoDAO.class);
+        when(networkInfoDao.getNetworkInterfaces(any(HostRef.class))).thenReturn(networkInfo);
+
+        when(daoFactory.getHostInfoDAO()).thenReturn(hostInfoDao);
+        when(daoFactory.getNetworkInterfaceInfoDAO()).thenReturn(networkInfoDao);
+
+        ApplicationContext.getInstance().setDAOFactory(daoFactory);
+
+        // Setup View
+        ArgumentCaptor<ActionListener> listenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        view = mock(HostOverviewView.class);
+        doNothing().when(view).addActionListener(listenerCaptor.capture());
+        ViewFactory viewFactory = mock(ViewFactory.class);
+        when(viewFactory.getView(eq(HostOverviewView.class))).thenReturn(view);
+
+        ApplicationContext.getInstance().setViewFactory(viewFactory);
+
+        HostOverviewController controller = new HostOverviewController(ref);
+
+        listener = listenerCaptor.getValue();
+        timerAction = timerActionCaptor.getValue();
+    }
+
+    @After
+    public void tearDown() {
+        ApplicationContextUtil.resetApplicationContext();
+    }
+
+    @Test
+    public void verifyViewIsUpdatedWithData() {
+        timerAction.run();
+
+        verify(view).setCpuCount(eq(String.valueOf(CPU_COUNT)));
+        verify(view).setCpuModel(eq(CPU_MODEL));
+        verify(view).setHostName(eq(HOST_NAME));
+
+        verify(view).setNetworkTableColumns(any(String[][].class));
+        verify(view).setInitialNetworkTableData(eq(new String[][] { new String[] { NETWORK_INTERFACE, IPV4_ADDR, IPV6_ADDR }, }));
+
+        verify(view).setOsKernel(eq(KERNEL_NAME));
+        verify(view).setOsName(eq(OS_NAME));
+        verify(view).setTotalMemory(eq(String.valueOf(TOTAL_MEMORY)));
+    }
+
+    @Test
+    public void verifyTimerIsSetUpCorrectly() {
+        assertNotNull(timer);
+
+        verify(timer).setAction(isNotNull(Runnable.class));
+        verify(timer).setDelay(5);
+        verify(timer).setTimeUnit(TimeUnit.SECONDS);
+        verify(timer).setInitialDelay(0);
+        verify(timer).setSchedulingType(SchedulingType.FIXED_RATE);
+    }
+
+    @Test
+    public void verifyTimerRunsWhenNeeded() {
+        listener.actionPerformed(new ActionEvent<>(view, Action.VISIBLE));
+
+        verify(timer).start();
+
+        listener.actionPerformed(new ActionEvent<>(view, Action.HIDDEN));
+
+        verify(timer).stop();
+    }
+
+}