changeset 588:591c74ae68d5

Add initial Thread Monitor Pie Chart reviewed-by: rkennke review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-September/003079.html
author Mario Torre <neugens.limasoftware@gmail.com>
date Fri, 07 Sep 2012 17:22:36 +0200
parents 22a2df282135
children f7953e6ce321
files client/swing-components/src/main/java/com/redhat/thermostat/swing/ChartPanel.java distribution/config/osgi-export.properties thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadDetailsView.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTableBean.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTableView.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadView.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadViewProvider.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/chart/ThreadDeatailsPieChart.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/monitor.png thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadDetailsView.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/ThreadAliveDaemonTimelinePanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadDetailsChart.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/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java
diffstat 23 files changed, 533 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/client/swing-components/src/main/java/com/redhat/thermostat/swing/ChartPanel.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/client/swing-components/src/main/java/com/redhat/thermostat/swing/ChartPanel.java	Fri Sep 07 17:22:36 2012 +0200
@@ -43,6 +43,7 @@
 
 import com.redhat.thermostat.charts.Chart;
 
+@SuppressWarnings("serial")
 public class ChartPanel extends JPanel {
 
     private Chart chart;
--- a/distribution/config/osgi-export.properties	Wed Sep 05 11:30:48 2012 +0200
+++ b/distribution/config/osgi-export.properties	Fri Sep 07 17:22:36 2012 +0200
@@ -46,6 +46,7 @@
 org.jfree.chart.plot
 org.jfree.chart.renderer.xy
 org.jfree.data
+org.jfree.data.general
 org.jfree.data.time
 org.jfree.data.xy
 org.jfree.ui
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadDetailsView.java	Fri Sep 07 17:22:36 2012 +0200
@@ -0,0 +1,60 @@
+/*
+ * 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.common;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.client.osgi.service.BasicView;
+import com.redhat.thermostat.client.ui.IconDescriptor;
+
+public abstract class ThreadDetailsView extends BasicView {
+    
+    private static final Logger logger = Logger.getLogger(ThreadDetailsView.class.getSimpleName());
+    
+    public IconDescriptor getEmptyDetailsIcon() {
+        try {
+            return IconDescriptor.createFromClassloader(ClassLoader.getSystemClassLoader(), "com/redhat/thermostat/thread/client/common/monitor.png");
+        } catch (IOException e) {
+            logger.log(Level.WARNING, "Can't load emptyDetailsIcon", e);
+        }
+        return null;
+    }
+    
+    public abstract void setDetails(ThreadTableBean thread);
+}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTableBean.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTableBean.java	Fri Sep 07 17:22:36 2012 +0200
@@ -47,9 +47,11 @@
     private long start;
     private long waitedCount;
     private long blockedCount;
-    
+
     private double runningPercent;
     private double waitingPercent;
+    private double monitorPercent;
+    private double sleepingPercent;
     
     public long getId() {
         return id;
@@ -145,4 +147,20 @@
     public void setRunningPercent(double runningPercent) {
         this.runningPercent = runningPercent;
     }
+    
+    public void setMonitorPercent(double monitorPercent) {
+        this.monitorPercent = monitorPercent;
+    }
+    
+    public double getMonitorPercent() {
+        return monitorPercent;
+    }
+    
+    public void setSleepingPercent(double sleepingPercent) {
+        this.sleepingPercent = sleepingPercent;
+    }
+    
+    public double getSleepingPercent() {
+        return sleepingPercent;
+    }
 }
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTableView.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTableView.java	Fri Sep 07 17:22:36 2012 +0200
@@ -39,8 +39,27 @@
 import java.util.List;
 
 import com.redhat.thermostat.client.osgi.service.BasicView;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
 
 public abstract class ThreadTableView extends BasicView {
 
+    public static enum ThreadSelectionAction {
+        SHOW_THREAD_DETAILS
+    }
+    
+    protected final ActionNotifier<ThreadSelectionAction> threadTableNotifier;
+    public ThreadTableView() {
+        threadTableNotifier = new ActionNotifier<>(this);
+    }
+    
+    public void addThreadSelectionActionListener(ActionListener<ThreadSelectionAction> listener) {
+        threadTableNotifier.addActionListener(listener);
+    }
+    
+    public void removeThreadSelectionActionListener(ActionListener<ThreadSelectionAction> listener) {
+        threadTableNotifier.removeActionListener(listener);
+    }
+    
     public abstract void display(List<ThreadTableBean> arrayList);
 }
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadView.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadView.java	Fri Sep 07 17:22:36 2012 +0200
@@ -81,4 +81,6 @@
         this.appService = appService;
         this.uniqueId = uniqueId;
     }
+
+    public abstract void displayThreadDetails(ThreadTableBean thread);
 }
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadViewProvider.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadViewProvider.java	Fri Sep 07 17:22:36 2012 +0200
@@ -37,7 +37,6 @@
 package com.redhat.thermostat.thread.client.common;
 
 import com.redhat.thermostat.client.osgi.service.ViewProvider;
-import com.redhat.thermostat.client.ui.UIComponent;
 
 public interface ThreadViewProvider extends ViewProvider {
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/chart/ThreadDeatailsPieChart.java	Fri Sep 07 17:22:36 2012 +0200
@@ -0,0 +1,86 @@
+/*
+ * 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.common.chart;
+
+import java.awt.Color;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.PiePlot3D;
+import org.jfree.data.general.DefaultPieDataset;
+
+import com.redhat.thermostat.charts.Chart;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
+
+public class ThreadDeatailsPieChart extends Chart {
+    
+    private ThreadTableBean thread;
+    
+    public ThreadDeatailsPieChart(ThreadTableBean thread) {
+        this.thread = thread;
+    }
+    
+    @Override
+    protected JFreeChart createChart(int width, int height, Color bgColor) {
+        
+        DefaultPieDataset dataset = new DefaultPieDataset();
+        
+        dataset.setValue("Running", thread.getRunningPercent());
+        dataset.setValue("Waiting", thread.getWaitingPercent());
+        dataset.setValue("Monitor", thread.getMonitorPercent());
+        dataset.setValue("Sleeping", thread.getSleepingPercent());
+        
+        JFreeChart chart = ChartFactory.createPieChart3D(
+                thread.getName(),       // chart title
+                dataset,                // data
+                true,                   // include legend
+                true,
+                false
+            );
+
+        chart.setAntiAlias(true);
+        
+        PiePlot3D plot = (PiePlot3D) chart.getPlot();
+        plot.setBackgroundPaint(bgColor);
+        
+        plot.setStartAngle(290);
+        plot.setForegroundAlpha(0.5f);
+        
+        
+        return chart;
+    }
+}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java	Fri Sep 07 17:22:36 2012 +0200
@@ -48,12 +48,15 @@
     BLOCK_COUNT,
     RUNNING,
     WAITING,
+    SLEEPING,
+    MONITOR,
     
     START_RECORDING,
     STOP_RECORDING,
     
     VM_CAPABILITIES,
     TABLE,
+    DETAILS,
     
     LIVE_THREADS,
     DAEMON_THREADS,
@@ -61,7 +64,8 @@
     THREAD_CONTROL_PANEL,
     THREAD_DUMP,
     
-    MISSING_INFO;
+    MISSING_INFO,
+    THREAD_DETAILS_EMTPY;
 
     public static final String RESOURCE_BUNDLE =
             "com.redhat.thermostat.thread.client.common.locale.strings";
--- a/thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties	Fri Sep 07 17:22:36 2012 +0200
@@ -8,14 +8,19 @@
 BLOCK_COUNT = Blocked Count
 RUNNING = Running %
 WAITING = Waiting %
-
+SLEEPING = Sleeping %
+MONITOR = Monitor %
+    
 START_RECORDING = Start Recording
 STOP_RECORDING = Stop Recording
 VM_CAPABILITIES = VM Capabilities
 TABLE = Table
+DETAILS = Details
 
 LIVE_THREADS = Live Threads
 DAEMON_THREADS = Daemon Threads
 
 THREAD_CONTROL_PANEL = Thread Control Panel
 THREAD_DUMP = Thread Dump
+
+THREAD_DETAILS_EMTPY = Please, select a Thread from the Thread Table
\ No newline at end of file
Binary file thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/monitor.png has changed
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Fri Sep 07 17:22:36 2012 +0200
@@ -41,10 +41,9 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.client.osgi.service.ApplicationCache;
 import com.redhat.thermostat.client.osgi.service.ApplicationService;
+import com.redhat.thermostat.client.osgi.service.BasicView.Action;
 import com.redhat.thermostat.client.osgi.service.VmInformationServiceController;
-import com.redhat.thermostat.client.osgi.service.BasicView.Action;
 import com.redhat.thermostat.client.ui.UIComponent;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
@@ -53,16 +52,16 @@
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
+import com.redhat.thermostat.thread.client.common.ThreadTableView;
+import com.redhat.thermostat.thread.client.common.ThreadTableView.ThreadSelectionAction;
 import com.redhat.thermostat.thread.client.common.ThreadView;
+import com.redhat.thermostat.thread.client.common.ThreadView.ThreadAction;
 import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
-import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView;
-import com.redhat.thermostat.thread.client.common.ThreadView.ThreadAction;
 import com.redhat.thermostat.thread.client.common.chart.LivingDaemonThreadDifferenceChart;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
-
 import com.redhat.thermostat.thread.model.ThreadSummary;
-import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class ThreadInformationController implements VmInformationServiceController {
 
@@ -190,13 +189,22 @@
         }
     }
     
+    private class ThreadSelectionActionListener implements ActionListener<ThreadSelectionAction> {
+        @Override
+        public void actionPerformed(ActionEvent<ThreadSelectionAction> actionEvent) {
+            view.displayThreadDetails((ThreadTableBean) actionEvent.getPayload());
+        }
+    }
+    
     private void initControllers() {
         CommonController capsController =
                 new VMThreadCapabilitiesController(view.createVMThreadCapabilitiesView(), collector);
         capsController.initialize();
         
+        ThreadTableView threadTableView = view.createThreadTableView();
+        threadTableView.addThreadSelectionActionListener(new ThreadSelectionActionListener());
         CommonController threadTableController =
-                new ThreadTableController(view.createThreadTableView(), collector,
+                new ThreadTableController(threadTableView, collector,
                                           ApplicationContext.getInstance().getTimerFactory().createTimer());
         threadTableController.initialize();
     }
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java	Fri Sep 07 17:22:36 2012 +0200
@@ -146,6 +146,8 @@
                     // time for some stats
                     double running = 0;
                     double waiting = 0;
+                    double monitor = 0;
+                    double sleeping = 0;
                     for (ThreadInfoData info : beanList) {
                         State state = info.getState();
                         switch (state) {
@@ -154,10 +156,13 @@
                             break;
                         case NEW:
                         case TERMINATED:
-                            System.err.println("yeah!");
                             break;
                         case BLOCKED:
+                            monitor++;
+                            break;
                         case TIMED_WAITING:
+                            sleeping++;
+                            break;
                         case WAITING:
                             waiting++;
                         default:
@@ -165,12 +170,18 @@
                         }
                     }
                     int polls = beanList.size();
-                    double runningPercent = (running/polls) * 100;
-                    double waitingPercent = (waiting/polls) * 100;
+                    double percent = (running/polls) * 100;
+                    bean.setRunningPercent(percent);
+
+                    percent = (waiting/polls) * 100;
+                    bean.setWaitingPercent(percent);
                     
-                    bean.setRunningPercent(runningPercent);
-                    bean.setWaitingPercent(waitingPercent);
-                    
+                    percent = (monitor/polls) * 100;
+                    bean.setMonitorPercent(percent);
+
+                    percent = (sleeping/polls) * 100;
+                    bean.setSleepingPercent(percent);
+
                     tableBeans.add(bean);
                 }
                 
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java	Fri Sep 07 17:22:36 2012 +0200
@@ -36,13 +36,11 @@
 
 package com.redhat.thermostat.thread.client.controller.impl;
 
-import static org.junit.Assert.*;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.times;
 
 import org.junit.After;
 import org.junit.Before;
@@ -59,7 +57,9 @@
 import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
 import com.redhat.thermostat.thread.client.common.ThreadTableView;
+import com.redhat.thermostat.thread.client.common.ThreadTableView.ThreadSelectionAction;
 import com.redhat.thermostat.thread.client.common.ThreadView;
 import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
 import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView;
@@ -71,12 +71,16 @@
     private Timer timer;
     private ThreadView view;
     private ActionListener<ThreadView.Action> actionListener;
-        
+    private ActionListener<ThreadTableView.ThreadSelectionAction> threadTableActionListener;
+
     private ThreadViewProvider viewFactory;
     private ThreadInformationController controller;
     
     private ApplicationService appService;
-        
+
+    private ThreadTableView threadTableView;
+    private VMThreadCapabilitiesView threadCapsView;
+    
     @Before
     public void setUp() {
         ApplicationContextUtil.resetApplicationContext();
@@ -90,15 +94,15 @@
     }
 
     private void setUpView() {
-        VMThreadCapabilitiesView commonController1 = mock(VMThreadCapabilitiesView.class);
-        ThreadTableView commonController2 = mock(ThreadTableView.class);
+        threadCapsView = mock(VMThreadCapabilitiesView.class);
+        threadTableView = mock(ThreadTableView.class);
 
         view = mock(ThreadView.class);
         viewFactory = mock(ThreadViewProvider.class);
         when(viewFactory.createView()).thenReturn(view);
         
-        when(view.createVMThreadCapabilitiesView()).thenReturn(commonController1);
-        when(view.createThreadTableView()).thenReturn(commonController2);
+        when(view.createVMThreadCapabilitiesView()).thenReturn(threadCapsView);
+        when(view.createThreadTableView()).thenReturn(threadTableView);
     }
     
     private void setUpTimers() {
@@ -112,12 +116,16 @@
     }
     
     private void setUpListeners() {        
-        ArgumentCaptor<ActionListener> viewArgumentCaptor1 = ArgumentCaptor.forClass(ActionListener.class);
-        doNothing().when(view).addActionListener(viewArgumentCaptor1.capture());
+        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addActionListener(viewArgumentCaptor.capture());
+        
+        ArgumentCaptor<ActionListener> threadTableViewCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(threadTableView).addThreadSelectionActionListener(threadTableViewCaptor.capture());
         
         createController();
         
-        actionListener = viewArgumentCaptor1.getValue();
+        actionListener = viewArgumentCaptor.getValue();
+        threadTableActionListener = threadTableViewCaptor.getValue();
     }
     
     private void createController() {
@@ -138,6 +146,15 @@
     }
     
     @Test
+    public void verifyViewCreateSubViewCalled() {
+        
+        createController();
+        
+        verify(view).createThreadTableView();
+        verify(view).createVMThreadCapabilitiesView();
+    }
+    
+    @Test
     public void verifyLiveRecording() {
         
         ActionListener<ThreadView.ThreadAction> threadActionListener;
@@ -199,4 +216,18 @@
         actionListener.actionPerformed(new ActionEvent<>(view, ThreadView.Action.HIDDEN));
         verify(timer).stop();
     }
+    
+    @Test
+    public void verifyTableViewLinksToDetailsView() {
+        setUpListeners();
+
+        ThreadTableBean bean = mock(ThreadTableBean.class);
+
+        ActionEvent<ThreadSelectionAction> event =
+                new ActionEvent<>(threadTableView, ThreadSelectionAction.SHOW_THREAD_DETAILS);
+        event.setPayload(bean);
+        
+        threadTableActionListener.actionPerformed(event);
+        verify(view).displayThreadDetails(bean);
+    }
 }
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java	Fri Sep 07 17:22:36 2012 +0200
@@ -38,12 +38,8 @@
 
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -55,7 +51,6 @@
 import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
 import com.redhat.thermostat.thread.client.common.ThreadTableView;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.model.ThreadInfoData;
 
 public class ThreadTableControllerTest {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadDetailsView.java	Fri Sep 07 17:22:36 2012 +0200
@@ -0,0 +1,91 @@
+/*
+ * 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.JPanel;
+
+import com.redhat.thermostat.client.ui.SwingComponent;
+import com.redhat.thermostat.common.locale.Translate;
+import com.redhat.thermostat.swing.ChartPanel;
+import com.redhat.thermostat.thread.client.common.ThreadDetailsView;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
+import com.redhat.thermostat.thread.client.common.chart.ThreadDeatailsPieChart;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import java.awt.GridBagLayout;
+import java.awt.GridBagConstraints;
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+
+public class SwingThreadDetailsView extends ThreadDetailsView implements SwingComponent {
+
+    private JPanel details;
+    private static final Translate t = LocaleResources.createLocalizer();
+
+    SwingThreadDetailsView() {
+        details = new JPanel();
+        details.setLayout(new BorderLayout(0, 0));
+        
+        JLabel lblNewLabel = new JLabel(t.localize(LocaleResources.THREAD_DETAILS_EMTPY));
+        lblNewLabel.setIcon(new ImageIcon(getEmptyDetailsIcon().getData().array()));
+        details.add(lblNewLabel);
+    }
+    
+    @Override
+    public Component getUiComponent() {
+        return details;
+    }
+
+    @Override
+    public void setDetails(ThreadTableBean thread) {
+        details.removeAll();
+        
+        ThreadDetailsChart threadChart = new ThreadDetailsChart();
+        
+        ChartPanel threadSummary = new ChartPanel(new ThreadDeatailsPieChart(thread));
+        threadChart.add(threadSummary);
+        
+        details.add(threadChart);
+        details.repaint();
+    }
+}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTableView.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTableView.java	Fri Sep 07 17:22:36 2012 +0200
@@ -37,12 +37,15 @@
 package com.redhat.thermostat.thread.client.swing.impl;
 
 import java.awt.Component;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
 import javax.swing.JTable;
 import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
 import javax.swing.event.TableModelEvent;
 import javax.swing.event.TableModelListener;
 import javax.swing.table.DefaultTableModel;
@@ -78,6 +81,7 @@
         });
         
         table = new JTable(new ThreadViewTableModel(new ArrayList<ThreadTableBean>()));
+        table.setName("threadBeansTable");
         table.getModel().addTableModelListener(new TableModelListener() {
             @Override
             public void tableChanged(TableModelEvent e) {
@@ -100,6 +104,27 @@
         table.setFillsViewportHeight(true);
         table.setAutoCreateRowSorter(true);
         tablePanel.setTable(table);
+        
+        table.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() == 2) {
+                    ThreadViewTableModel model = (ThreadViewTableModel) table.getModel();
+                    int selectedRow = table.getSelectedRow();
+                    if (selectedRow != -1) {
+                        selectedRow = table.convertRowIndexToModel(selectedRow);
+                        final ThreadTableBean bean = model.infos.get(selectedRow);
+                        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                            protected Void doInBackground() throws Exception {
+                                threadTableNotifier.fireAction(ThreadSelectionAction.SHOW_THREAD_DETAILS, bean);
+                                return null;
+                            }
+                        };
+                        worker.execute();
+                    }
+                }
+            }
+        });
     }
     
     @Override
@@ -153,7 +178,9 @@
                 t.localize(LocaleResources.WAIT_COUNT),
                 t.localize(LocaleResources.BLOCK_COUNT),
                 t.localize(LocaleResources.RUNNING),
-                t.localize(LocaleResources.WAITING), //, "Heap", "CPU Time", "User CPU Time"
+                t.localize(LocaleResources.WAITING),
+                t.localize(LocaleResources.SLEEPING),
+                t.localize(LocaleResources.MONITOR), //, "Heap", "CPU Time", "User CPU Time"
         };
         
         private List<ThreadTableBean> infos;
@@ -233,7 +260,13 @@
                 break;
             case 7:
                 result = info.getWaitingPercent();
-                break;                
+                break;
+            case 8:
+                result = info.getSleepingPercent();
+                break;
+            case 9:
+                result = info.getMonitorPercent();
+                break;
              default:
                  result = "n/a";
                  break;
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java	Fri Sep 07 17:22:36 2012 +0200
@@ -54,6 +54,8 @@
 import com.redhat.thermostat.client.ui.SwingComponent;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.swing.ChartPanel;
+import com.redhat.thermostat.thread.client.common.ThreadDetailsView;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
 import com.redhat.thermostat.thread.client.common.ThreadTableView;
 import com.redhat.thermostat.thread.client.common.ThreadView;
 import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView;
@@ -67,13 +69,18 @@
     private ThreadMainPanel panel;
     private ThreadAliveDaemonTimelinePanel timelinePanel;
     
-    private SwingThreadTableView threadTable;
+    private SwingThreadTableView threadTableView;
     private SwingVMThreadCapabilitiesView vmCapsView;
+    private SwingThreadDetailsView threadDetailsView;
+    
+    private JTabbedPane pane;
     
     private static final Translate t = LocaleResources.createLocalizer();
 
     private boolean skipNotification = false;
     
+    private int threadDetailsPaneID = 0;
+    
     public SwingThreadView() {
         
         panel = new ThreadMainPanel();
@@ -135,11 +142,17 @@
         panel.getSplitPane().setTopComponent(timelinePanel);
         
         vmCapsView = new SwingVMThreadCapabilitiesView();
-        JTabbedPane pane = new JTabbedPane();
+        pane = new JTabbedPane();
+        pane.setName("tabbedPane");
+        
         pane.addTab(t.localize(LocaleResources.VM_CAPABILITIES), vmCapsView.getUiComponent());
         
-        threadTable = new SwingThreadTableView();
-        pane.addTab(t.localize(LocaleResources.TABLE), threadTable.getUiComponent());
+        threadTableView = new SwingThreadTableView();
+        pane.addTab(t.localize(LocaleResources.TABLE), threadTableView.getUiComponent());
+        
+        threadDetailsView = new SwingThreadDetailsView();
+        pane.addTab(t.localize(LocaleResources.DETAILS), threadDetailsView.getUiComponent());
+        threadDetailsPaneID = 2;
         
         panel.getSplitPane().setBottomComponent(pane);
     }
@@ -210,7 +223,7 @@
     
     @Override
     public ThreadTableView createThreadTableView() {
-        return threadTable;
+        return threadTableView;
     }
     
     @Override
@@ -239,4 +252,15 @@
             appService.getApplicationCache().addAttribute(DIVIDER_LOCATION_KEY, location);
         }
     }
+    
+    @Override
+    public void displayThreadDetails(final ThreadTableBean thread) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                pane.setSelectedIndex(threadDetailsPaneID);
+                threadDetailsView.setDetails(thread);
+            }
+        });
+    }
 }
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadAliveDaemonTimelinePanel.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadAliveDaemonTimelinePanel.java	Fri Sep 07 17:22:36 2012 +0200
@@ -41,7 +41,6 @@
 import javax.swing.GroupLayout.Alignment;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
-import javax.swing.JToggleButton;
 import javax.swing.LayoutStyle.ComponentPlacement;
 import javax.swing.SwingConstants;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadDetailsChart.java	Fri Sep 07 17:22:36 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.impl;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+
+@SuppressWarnings("serial")
+public class ThreadDetailsChart extends JPanel {
+    
+    public ThreadDetailsChart() {
+        setOpaque(false);
+        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+    }
+}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java	Fri Sep 07 17:22:36 2012 +0200
@@ -62,6 +62,7 @@
         headerPanel.setContent(content);
         
         splitPane = new JSplitPane();
+        splitPane.setName("threadMainPanelSplitPane");
         splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
         splitPane.setOneTouchExpandable(true);
         
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTable.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTable.java	Fri Sep 07 17:22:36 2012 +0200
@@ -41,6 +41,7 @@
 import javax.swing.JScrollPane;
 import javax.swing.JTable;
 
+@SuppressWarnings("serial")
 public class ThreadTable extends JPanel {
     
     private JScrollPane scrollPane;
--- a/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java	Wed Sep 05 11:30:48 2012 +0200
+++ b/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java	Fri Sep 07 17:22:36 2012 +0200
@@ -36,22 +36,27 @@
 
 package com.redhat.thermostat.thread.client.swing.impl;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.Semaphore;
 
-import javax.swing.JButton;
 import javax.swing.JFrame;
 
 import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
 
 import org.fest.swing.annotation.GUITest;
+import org.fest.swing.data.TableCell;
 import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
 import org.fest.swing.edt.GuiActionRunner;
 import org.fest.swing.edt.GuiTask;
 import org.fest.swing.fixture.FrameFixture;
-import org.fest.swing.fixture.JButtonFixture;
 import org.fest.swing.fixture.JLabelFixture;
 import org.fest.swing.fixture.JToggleButtonFixture;
 import org.junit.After;
@@ -61,7 +66,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.locale.Translate;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
+import com.redhat.thermostat.thread.client.common.ThreadTableView;
+import com.redhat.thermostat.thread.client.common.ThreadTableView.ThreadSelectionAction;
 import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
 
 @RunWith(CacioFESTRunner.class)
@@ -132,4 +142,44 @@
         toggleTest.requireText(t.localize(LocaleResources.START_RECORDING) + ":");
     }
 
+    @GUITest
+    @Test
+    public void verifTableViewLinksToDetailsView() throws InvocationTargetException, InterruptedException {
+        
+        final boolean listenerCalled[] = new boolean[1];
+        
+        ThreadTableBean bean1 = mock(ThreadTableBean.class);
+        when(bean1.getName()).thenReturn("mocked bean 1");
+        
+        ThreadTableBean bean2 = mock(ThreadTableBean.class);
+        when(bean2.getName()).thenReturn("mocked bean 2");
+        
+        List<ThreadTableBean> threadList = new ArrayList<>();
+        threadList.add(bean1);
+        threadList.add(bean2);
+
+        frameFixture.show();
+        
+        frameFixture.splitPane("threadMainPanelSplitPane").moveDividerTo(0);
+        frameFixture.tabbedPane("tabbedPane").selectTab(1);
+        
+        final Semaphore sem = new Semaphore(1);
+        ThreadTableView tableView = view.createThreadTableView();
+        tableView.addThreadSelectionActionListener(new ActionListener<ThreadTableView.ThreadSelectionAction>() {
+            @Override
+            public void actionPerformed(ActionEvent<ThreadSelectionAction> actionEvent) {
+                listenerCalled[0] = true;
+                view.displayThreadDetails((ThreadTableBean) actionEvent.getPayload());
+                sem.release();
+            }
+        });
+        
+        tableView.display(threadList);
+        
+        frameFixture.table("threadBeansTable").cell(TableCell.row(1).column(0)).doubleClick();
+        sem.acquire();
+        
+        assertTrue(listenerCalled[0]);
+        assertEquals(2, frameFixture.tabbedPane("tabbedPane").target.getSelectedIndex());
+    }
 }