changeset 545:bf3f8d8990bb

Client - swing review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-August/002721.html reviewed-by: omajid PR 1107
author Mario Torre <neugens.limasoftware@gmail.com>
date Fri, 17 Aug 2012 12:54:25 +0200
parents ed971fd48ad4
children 5ad8ad98f110
files thread/client-swing/pom.xml thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/SwingThreadViewService.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTableView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVMThreadCapabilitiesView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadAliveDaemonTimelinePanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTable.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/VMCapsSummaryPanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/osgi/Activator.java
diffstat 10 files changed, 1159 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/pom.xml	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-thread</artifactId>
+    <version>0.4.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-thread-client-swing</artifactId>
+  <packaging>bundle</packaging>
+
+  <name>Thermostat Thread Info Swing Client</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-thread-client-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-Activator>com.redhat.thermostat.thread.client.swing.osgi.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.thread.client.swing,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.thread.client.swing.osgi,
+              com.redhat.thermostat.thread.client.swing.impl,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/SwingThreadViewService.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,49 @@
+/*
+ * 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.thread.client.swing;
+
+import com.redhat.thermostat.thread.client.common.ThreadView;
+import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
+import com.redhat.thermostat.thread.client.swing.impl.SwingThreadView;
+
+public class SwingThreadViewService implements ThreadViewProvider {
+    
+    @Override
+    public ThreadView createView() {
+        return new SwingThreadView();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTableView.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,235 @@
+/*
+ * 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.thread.client.swing.impl;
+
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.DefaultTableModel;
+
+import com.redhat.thermostat.client.ui.ComponentVisibleListener;
+import com.redhat.thermostat.client.ui.SwingComponent;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
+import com.redhat.thermostat.thread.client.common.ThreadTableView;
+
+public class SwingThreadTableView extends ThreadTableView implements SwingComponent {
+
+    private int currentSelection = -1;
+    
+    private JTable table;
+    private ThreadTable tablePanel;
+    
+    public SwingThreadTableView() {
+        tablePanel = new ThreadTable();
+        tablePanel.addHierarchyListener(new ComponentVisibleListener() {
+            @Override
+            public void componentShown(Component component) {
+                SwingThreadTableView.this.notify(Action.VISIBLE);
+            }
+            
+            @Override
+            public void componentHidden(Component component) {
+                SwingThreadTableView.this.notify(Action.HIDDEN);
+            }
+        });
+        
+        table = new JTable(new ThreadViewTableModel(new ArrayList<ThreadTableBean>()));
+        table.getModel().addTableModelListener(new TableModelListener() {
+            @Override
+            public void tableChanged(TableModelEvent e) {
+                // NOTE: The fireTableDataChanged executes this listener
+                // before the internal listener, this means this update will
+                // be overridden since the default listener resets the model.
+                // So, although we are in the EDT, we need to ensure that
+                // we schedule this operation for later, rather than do it
+                // right away... isn't Swing fun?
+                SwingUtilities.invokeLater(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (currentSelection != -1) {
+                            table.setRowSelectionInterval(currentSelection, currentSelection);
+                        }
+                    }
+                });
+            }
+        });
+        table.setFillsViewportHeight(true);
+        table.setAutoCreateRowSorter(true);
+        tablePanel.setTable(table);
+    }
+    
+    @Override
+    public Component getUiComponent() {
+        return tablePanel;
+    }
+    
+    @Override
+    public void display(final List<ThreadTableBean> infos) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                
+                // reset the selection for the next iteration
+                // everything is happening in one thread, so there's no fear
+                currentSelection = -1;
+                
+                ThreadViewTableModel model = (ThreadViewTableModel) table.getModel();
+                int selectedRow = table.getSelectedRow();
+                
+                ThreadTableBean info = null;
+                if (selectedRow != -1) {
+                    info = model.infos.get(selectedRow);
+                }
+                
+                model.infos = infos;
+
+                if (info != null) {
+                    int index = 0;
+                    for (ThreadTableBean inModel : model.infos) {
+                        if (info.equals(inModel)) {
+                            currentSelection = index;
+                            break;
+                        }
+                        index++;
+                    }
+                }
+                model.fireTableDataChanged();
+            }
+        });
+    }
+
+    @SuppressWarnings("serial")
+    private class ThreadViewTableModel extends DefaultTableModel {
+
+        // TODO: localize
+        private String [] columns = {
+                "Name", "ID", "Start", "Stop", "Wait Count", "Blocked Count", "Running %", "Waiting %"//, "Heap", "CPU Time", "User CPU Time"
+        };
+        
+        private List<ThreadTableBean> infos;
+        public ThreadViewTableModel(List<ThreadTableBean> infos) {
+            this.infos = infos;
+        }
+    
+        @Override
+        public String getColumnName(int column) {
+            return columns[column];
+        }
+        
+        @Override
+        public int getColumnCount() {
+            return columns.length;
+        }
+        
+        @Override
+        public int getRowCount() {
+            if (infos == null) {
+                return 0;
+            }
+            return infos.size();
+        }
+        
+        @Override
+        public boolean isCellEditable(int row, int column) {
+            return false;
+        }
+        
+        @Override
+        public Class<?> getColumnClass(int column) {
+            switch (column) {
+            case 0:
+            case 2:
+            case 3:                
+                return String.class;                
+            case 6:
+            case 7:
+                return Double.class;
+            default:
+                return Long.class;
+            }
+        }
+        
+        @Override
+        public Object getValueAt(int row, int column) {
+
+            Object result = null;
+            
+            ThreadTableBean info = infos.get(row);
+            switch (column) {
+            case 0:
+                result = info.getName();
+                break;
+            case 1:
+                result = info.getId();
+                break;
+            case 2:
+                result = new Date(info.getStartTimeStamp()).toString();
+                break;
+            case 3:
+                if (info.getStopTimeStamp() != 0) {
+                    result = new Date(info.getStopTimeStamp()).toString();
+                } else {
+                    result = "-";
+                }
+                break;
+            case 4:
+                result = info.getWaitedCount();
+                break;
+            case 5:
+                result = info.getBlockedCount();
+                break;
+            case 6:
+                result = info.getRunningPercent();
+                break;
+            case 7:
+                result = info.getWaitingPercent();
+                break;                
+             default:
+                 result = "n/a";
+                 break;
+            }
+            return result;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,178 @@
+/*
+ * 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.thread.client.swing.impl;
+
+import java.awt.Component;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+
+import com.redhat.thermostat.client.ui.ComponentVisibleListener;
+import com.redhat.thermostat.client.ui.SwingComponent;
+import com.redhat.thermostat.swing.ChartPanel;
+
+import com.redhat.thermostat.thread.client.common.ThreadTableView;
+import com.redhat.thermostat.thread.client.common.ThreadView;
+import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView;
+
+import com.redhat.thermostat.thread.client.common.chart.LivingDaemonThreadDifferenceChart;
+
+public class SwingThreadView extends ThreadView implements SwingComponent {
+    
+    private ThreadMainPanel panel;
+    private ThreadAliveDaemonTimelinePanel timelinePanel;
+    
+    private SwingThreadTableView threadTable;
+    private SwingVMThreadCapabilitiesView vmCapsView;
+    
+    public SwingThreadView() {
+        
+        panel = new ThreadMainPanel();
+        panel.addHierarchyListener(new ComponentVisibleListener() {
+            
+            @Override
+            public void componentShown(Component component) {
+                SwingThreadView.this.notify(Action.VISIBLE);
+                
+                // TODO: allow controller to define this value based on last
+                // user setting
+                panel.getSplitPane().setDividerLocation(0.30);
+            }
+            
+            @Override
+            public void componentHidden(Component component) {
+                SwingThreadView.this.notify(Action.HIDDEN);
+            }
+        });
+        
+        panel.getLiveRecording().setText("Start Recording");
+        panel.getLiveRecording().addItemListener(new ItemListener() {
+            @Override
+            public void itemStateChanged(ItemEvent e) {
+                ThreadAction action = null;
+                if (e.getStateChange() == ItemEvent.SELECTED) {
+                    action = ThreadAction.START_LIVE_RECORDING;
+                    panel.getLiveRecording().setText("Recording probes...");
+                } else {
+                    action = ThreadAction.STOP_LIVE_RECORDING;
+                    panel.getLiveRecording().setText("Start Recording");
+                }
+                final ThreadAction toNotify = action;
+                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                    @Override
+                    protected Void doInBackground() throws Exception {
+                        notifier.fireAction(toNotify);
+                        return null;
+                    }
+                };
+                worker.execute();
+            }
+        });
+
+        timelinePanel = new ThreadAliveDaemonTimelinePanel();
+        panel.getSplitPane().setTopComponent(timelinePanel);
+        
+        vmCapsView = new SwingVMThreadCapabilitiesView();
+        JTabbedPane pane = new JTabbedPane();
+        pane.addTab("Capabilites", vmCapsView.getUiComponent());
+        
+        threadTable = new SwingThreadTableView();
+        pane.addTab("Table", threadTable.getUiComponent());
+        
+        panel.getSplitPane().setBottomComponent(pane);
+    }
+    
+    @Override
+    public Component getUiComponent() {
+        return panel;
+    }
+    
+    @Override
+    public void setRecording(final boolean recording) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                panel.getLiveRecording().setSelected(recording);
+            }
+        });
+    }
+    
+    @Override
+    public void setDaemonThreads(final String daemonThreads) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                timelinePanel.getDaemonThreads().setText(daemonThreads);
+            }
+        });
+    }
+    
+    public void setLiveThreads(String liveThreads) {
+        timelinePanel.getLiveThreads().setText(liveThreads);
+    };
+    
+    @Override
+    public void updateLivingDaemonTimeline(final LivingDaemonThreadDifferenceChart model)
+    {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                JPanel pane = timelinePanel.getTimelinePanel();
+                pane.removeAll();
+                
+                ChartPanel charts = new ChartPanel(model);
+                pane.add(charts);
+                pane.revalidate();
+                pane.repaint();
+            }
+        });
+    }
+    
+    @Override
+    public VMThreadCapabilitiesView createVMThreadCapabilitiesView() {
+        return vmCapsView;
+    }
+    
+    @Override
+    public ThreadTableView createThreadTableView() {
+        return threadTable;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVMThreadCapabilitiesView.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,97 @@
+/*
+ * 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.thread.client.swing.impl;
+
+import java.awt.Component;
+
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+
+import com.redhat.thermostat.client.ui.ComponentVisibleListener;
+import com.redhat.thermostat.client.ui.SwingComponent;
+import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView;
+import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+
+public class SwingVMThreadCapabilitiesView extends VMThreadCapabilitiesView implements SwingComponent {
+
+    private VMCapsSummaryPanel panel;
+    
+    public SwingVMThreadCapabilitiesView() {
+        panel = new VMCapsSummaryPanel();
+        panel.addHierarchyListener(new ComponentVisibleListener() {
+            
+            @Override
+            public void componentShown(Component component) {
+                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                    @Override
+                    protected Void doInBackground() throws Exception {
+                        SwingVMThreadCapabilitiesView.this.notify(Action.VISIBLE);
+                        return null;
+                    }
+                };
+                worker.execute();
+            }
+            
+            @Override
+            public void componentHidden(Component component) {
+                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                    @Override
+                    protected Void doInBackground() throws Exception {
+                        SwingVMThreadCapabilitiesView.this.notify(Action.HIDDEN);
+                        return null;
+                    }
+                };
+                worker.execute();
+            }
+        });
+    }
+    
+    @Override
+    public void setVMThreadCapabilities(final VMThreadCapabilities caps) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                panel.addInfo(caps.getSupportedFeaturesList());
+            }
+        });
+    }
+    
+    @Override
+    public Component getUiComponent() {
+        return panel;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadAliveDaemonTimelinePanel.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,135 @@
+/*
+ * 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.thread.client.swing.impl;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
+
+@SuppressWarnings("serial")
+class ThreadAliveDaemonTimelinePanel extends JPanel {
+    private JLabel liveThreads;
+    private JLabel daemonThreads;
+    private JPanel timelinePanel;
+
+    /**
+     * Create the panel.
+     */
+    public ThreadAliveDaemonTimelinePanel() {
+        
+        JPanel runningPanel = new JPanel();
+        runningPanel.setOpaque(false);
+        
+        timelinePanel = new JPanel();
+        timelinePanel.setOpaque(false);
+        timelinePanel.setLayout(new BoxLayout(timelinePanel, BoxLayout.X_AXIS));
+        
+        GroupLayout groupLayout = new GroupLayout(this);
+        groupLayout.setHorizontalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addGroup(groupLayout.createSequentialGroup()
+                    .addContainerGap()
+                    .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
+                        .addComponent(runningPanel, GroupLayout.DEFAULT_SIZE, 426, Short.MAX_VALUE)
+                        .addComponent(timelinePanel, GroupLayout.DEFAULT_SIZE, 426, Short.MAX_VALUE))
+                    .addContainerGap())
+        );
+        groupLayout.setVerticalGroup(
+            groupLayout.createParallelGroup(Alignment.TRAILING)
+                .addGroup(Alignment.LEADING, groupLayout.createSequentialGroup()
+                    .addContainerGap()
+                    .addComponent(runningPanel, GroupLayout.PREFERRED_SIZE, 55, GroupLayout.PREFERRED_SIZE)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(timelinePanel, GroupLayout.DEFAULT_SIZE, 233, Short.MAX_VALUE)
+                    .addGap(3))
+        );
+        
+        JLabel liveThreadsLabel = new JLabel("Live Threads:");
+        
+        JLabel daemonThreadsLabel = new JLabel("Daemon Threads:");
+        
+        liveThreads = new JLabel("-");
+        liveThreads.setHorizontalAlignment(SwingConstants.RIGHT);
+        
+        daemonThreads = new JLabel("-");
+        daemonThreads.setHorizontalAlignment(SwingConstants.RIGHT);
+        GroupLayout gl_runningPanel = new GroupLayout(runningPanel);
+        gl_runningPanel.setHorizontalGroup(
+            gl_runningPanel.createParallelGroup(Alignment.LEADING)
+                .addGroup(gl_runningPanel.createSequentialGroup()
+                    .addGroup(gl_runningPanel.createParallelGroup(Alignment.TRAILING, false)
+                        .addComponent(liveThreadsLabel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(daemonThreadsLabel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_runningPanel.createParallelGroup(Alignment.LEADING, false)
+                        .addComponent(daemonThreads, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(liveThreads, GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE))
+                    .addContainerGap(221, Short.MAX_VALUE))
+        );
+        gl_runningPanel.setVerticalGroup(
+            gl_runningPanel.createParallelGroup(Alignment.LEADING)
+                .addGroup(gl_runningPanel.createSequentialGroup()
+                    .addGroup(gl_runningPanel.createParallelGroup(Alignment.BASELINE)
+                        .addComponent(liveThreadsLabel)
+                        .addComponent(liveThreads))
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(gl_runningPanel.createParallelGroup(Alignment.BASELINE)
+                        .addComponent(daemonThreadsLabel)
+                        .addComponent(daemonThreads))
+                    .addContainerGap(15, Short.MAX_VALUE))
+        );
+        runningPanel.setLayout(gl_runningPanel);
+        setLayout(groupLayout);
+
+    }
+    
+    public JLabel getLiveThreads() {
+        return liveThreads;
+    }
+    
+    public JLabel getDaemonThreads() {
+        return daemonThreads;
+    }
+    
+    public JPanel getTimelinePanel() {
+        return timelinePanel;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,135 @@
+/*
+ * 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.thread.client.swing.impl;
+
+import javax.swing.BoxLayout;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.JButton;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JToggleButton;
+import javax.swing.LayoutStyle.ComponentPlacement;
+
+import com.redhat.thermostat.swing.HeaderPanel;
+
+@SuppressWarnings("serial")
+class ThreadMainPanel extends JPanel {
+
+    private JToggleButton liveRecording;
+    private JButton snapshot;
+    private JSplitPane splitPane;
+    
+    public ThreadMainPanel() {
+        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+        
+        HeaderPanel headerPanel = new HeaderPanel();
+        headerPanel.setHeader("Thread Control Panel");
+        
+        JPanel content = new JPanel();
+        headerPanel.setContent(content);
+        
+        JPanel controlPanel = new JPanel();
+        
+        splitPane = new JSplitPane();
+        splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
+        splitPane.setOneTouchExpandable(true);
+        
+        GroupLayout gl_content = new GroupLayout(content);
+        gl_content.setHorizontalGroup(
+            gl_content.createParallelGroup(Alignment.TRAILING)
+                .addGroup(gl_content.createSequentialGroup()
+                    .addContainerGap()
+                    .addComponent(splitPane, GroupLayout.DEFAULT_SIZE, 363, Short.MAX_VALUE)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(controlPanel, GroupLayout.PREFERRED_SIZE, 260, GroupLayout.PREFERRED_SIZE)
+                    .addContainerGap())
+        );
+        gl_content.setVerticalGroup(
+            gl_content.createParallelGroup(Alignment.LEADING)
+                .addGroup(Alignment.TRAILING, gl_content.createSequentialGroup()
+                    .addContainerGap()
+                    .addGroup(gl_content.createParallelGroup(Alignment.TRAILING)
+                        .addComponent(splitPane, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 313, Short.MAX_VALUE)
+                        .addComponent(controlPanel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 323, Short.MAX_VALUE))
+                    .addContainerGap())
+        );
+        
+        snapshot = new JButton("Thread Dump");
+        
+        liveRecording = new JToggleButton();
+        
+        JScrollPane scrollPane = new JScrollPane();
+        GroupLayout gl_controlPanel = new GroupLayout(controlPanel);
+        gl_controlPanel.setHorizontalGroup(
+            gl_controlPanel.createParallelGroup(Alignment.LEADING)
+                .addComponent(liveRecording, GroupLayout.DEFAULT_SIZE, 191, Short.MAX_VALUE)
+                .addComponent(snapshot, GroupLayout.DEFAULT_SIZE, 191, Short.MAX_VALUE)
+                .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 191, Short.MAX_VALUE)
+        );
+        gl_controlPanel.setVerticalGroup(
+            gl_controlPanel.createParallelGroup(Alignment.LEADING)
+                .addGroup(gl_controlPanel.createSequentialGroup()
+                    .addComponent(liveRecording)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(snapshot)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 251, Short.MAX_VALUE))
+        );
+        
+        JList<String> list = new JList<>();
+        scrollPane.setViewportView(list);
+        controlPanel.setLayout(gl_controlPanel);
+        
+        content.setLayout(gl_content);
+        
+        add(headerPanel);
+    }
+    
+    JToggleButton getLiveRecording() {
+        return liveRecording;
+    }
+    
+    JButton getSnapshot() {
+        return snapshot;
+    }
+    public JSplitPane getSplitPane() {
+        return splitPane;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTable.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,61 @@
+/*
+ * 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.thread.client.swing.impl;
+
+import javax.swing.JPanel;
+import javax.swing.BoxLayout;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+public class ThreadTable extends JPanel {
+    
+    private JScrollPane scrollPane;
+    
+    /**
+     * Create the panel.
+     */
+    public ThreadTable() {
+        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+        
+        scrollPane = new JScrollPane();
+        add(scrollPane);
+    }
+
+    void setTable(JTable table) {
+        scrollPane.setViewportView(table);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/VMCapsSummaryPanel.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,87 @@
+/*
+ * 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.thread.client.swing.impl;
+
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.util.List;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.border.TitledBorder;
+
+import com.redhat.thermostat.swing.models.NullSelectionModel;
+
+@SuppressWarnings("serial")
+class VMCapsSummaryPanel extends JPanel {
+
+    private DefaultListModel<String> listModel;
+
+    /**
+     * Create the panel.
+     */
+    public VMCapsSummaryPanel() {
+        setLayout(new GridLayout(0, 1, 0, 0));
+        setOpaque(false);
+        
+        listModel = new DefaultListModel<>();
+        
+        JPanel panel = new JPanel();
+        panel.setLayout(new GridLayout(0, 1, 0, 0));
+        panel.setBorder(new TitledBorder(null, "VM Capabilities", TitledBorder.RIGHT, TitledBorder.TOP, null, null));
+        panel.setOpaque(false);
+        
+        add(panel);
+        
+        JList<String> list = new JList<String>();
+        list.setPreferredSize(new Dimension(0, 0));
+        list.setOpaque(false);
+        list.setLayoutOrientation(JList.VERTICAL_WRAP);
+        list.setSelectionModel(new NullSelectionModel());
+        
+        list.setModel(listModel);
+        
+        panel.add(list);
+    }
+    
+    void addInfo(List<String> infos) {
+        listModel.removeAllElements();
+        for (String info : infos) {
+            listModel.addElement(info);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/osgi/Activator.java	Fri Aug 17 12:54:25 2012 +0200
@@ -0,0 +1,68 @@
+/*
+ * 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.thread.client.swing.osgi;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.client.osgi.service.ApplicationService;
+import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
+import com.redhat.thermostat.thread.client.swing.SwingThreadViewService;
+
+public class Activator implements BundleActivator {
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        ServiceTracker tracker = new ServiceTracker(context, ApplicationService.class.getName(), null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                
+                context.registerService(ThreadViewProvider.class.getName(), new SwingThreadViewService(), null);
+                
+                return super.addingService(reference);
+            }
+        };
+        tracker.open();
+    }
+    
+    @Override
+    public void stop(BundleContext context) throws Exception {}
+}