changeset 1138:f747a057d7a1

Add back checking for deadlocks Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-May/006883.html
author Omair Majid <omajid@redhat.com>
date Fri, 07 Jun 2013 11:29:06 -0400
parents 3a2501017e91
children 85cd7afdf70f
files thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/ThreadCollector.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadMXBeanCollector.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/VmDeadLockView.java thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/LocaleResources.java 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/VmDeadLockController.java thread/client-controllers/src/main/resources/com/redhat/thermostat/thread/client/controller/impl/strings.properties 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/VmDeadLockControllerTest.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/SwingVmDeadLockView.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/HarvesterCommand.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/VmDeadLockData.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java
diffstat 23 files changed, 923 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/ThreadCollector.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/ThreadCollector.java	Fri Jun 07 11:29:06 2013 -0400
@@ -43,6 +43,7 @@
 import com.redhat.thermostat.thread.model.ThreadInfoData;
 import com.redhat.thermostat.thread.model.ThreadSummary;
 import com.redhat.thermostat.thread.model.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
 
 public interface ThreadCollector {
     
@@ -71,5 +72,14 @@
      * descending order their by {@link ThreadInfoData#getTimeStamp()}.
      */
     List<ThreadInfoData> getThreadInfo();
+
+    /**
+     * Check for deadlocks. {@link #getLatestDeadLockData} needs to be called to
+     * obtain the data
+     */
+    void requestDeadLockCheck();
+
+    /** Return the latest deadlock data */
+    VmDeadLockData getLatestDeadLockData();
 }
 
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadMXBeanCollector.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadMXBeanCollector.java	Fri Jun 07 11:29:06 2013 -0400
@@ -61,6 +61,7 @@
 import com.redhat.thermostat.thread.model.ThreadInfoData;
 import com.redhat.thermostat.thread.model.ThreadSummary;
 import com.redhat.thermostat.thread.model.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
 
 public class ThreadMXBeanCollector implements ThreadCollector {
     
@@ -107,31 +108,8 @@
         harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.START.name());
         harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getIdString());
         
-        final CountDownLatch latch = new CountDownLatch(1);
-        final boolean[] result = new boolean[1];
-        
-        harvester.addListener(new RequestResponseListener() {
-            @Override
-            public void fireComplete(Request request, Response response) {
-                switch (response.getType()) {
-                case OK:
-                    result[0] = true;
-                    break;
-                default:
-                    break;
-                }
-                latch.countDown();
-            }
-        });
-        
-        try {
-            enqueueRequest(harvester);
-            latch.await();
-        } catch (CommandChannelException e) {
-            logger.log(Level.WARNING, "Failed to enqueue request", e);
-        } catch (InterruptedException ignore) {}
-        
-        return result[0];
+        return postAndWait(harvester);
+
     }
 
     @Override
@@ -142,32 +120,10 @@
         harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.STOP.name());
         harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getIdString());
 
-        final CountDownLatch latch = new CountDownLatch(1);
-        final boolean[] result = new boolean[1];
+        boolean result = postAndWait(harvester);
+        return result;
+    }
 
-        harvester.addListener(new RequestResponseListener() {
-            @Override
-            public void fireComplete(Request request, Response response) {
-                switch (response.getType()) {
-                case OK:
-                    result[0] = true;
-                    break;
-                default:
-                    break;
-                }
-                latch.countDown();
-            }
-        });
-        
-        try {
-            enqueueRequest(harvester);
-            latch.await();
-        } catch (CommandChannelException e) {
-            logger.log(Level.WARNING, "Failed to enqueue request", e);
-        } catch (InterruptedException ignore) {}
-        return result[0];
-    }
-    
     @Override
     public boolean isHarvesterCollecting() {
         ThreadHarvestingStatus status = threadDao.getLatestHarvestingStatus(ref);
@@ -213,6 +169,48 @@
         VMThreadCapabilities caps = threadDao.loadCapabilities(ref);
         return caps;
     }
+
+    @Override
+    public VmDeadLockData getLatestDeadLockData() {
+        return threadDao.loadLatestDeadLockStatus(ref);
+    }
+
+    @Override
+    public void requestDeadLockCheck() {
+        Request harvester = createRequest();
+        harvester.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME);
+        harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.FIND_DEADLOCKS.name());
+        harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getIdString());
+
+        postAndWait(harvester);
+    }
+
+    private boolean postAndWait(Request harvester) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final boolean[] result = new boolean[1];
+
+        harvester.addListener(new RequestResponseListener() {
+            @Override
+            public void fireComplete(Request request, Response response) {
+                switch (response.getType()) {
+                case OK:
+                    result[0] = true;
+                    break;
+                default:
+                    break;
+                }
+                latch.countDown();
+            }
+        });
+
+        try {
+            enqueueRequest(harvester);
+            latch.await();
+        } catch (CommandChannelException e) {
+            logger.log(Level.WARNING, "Failed to enqueue request", e);
+        } catch (InterruptedException ignore) {}
+        return result[0];
+    }
     
     private void enqueueRequest(Request req) throws CommandChannelException {
         ServiceReference ref = context.getServiceReference(RequestQueue.class.getName());
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java	Fri Jun 07 11:29:06 2013 -0400
@@ -56,6 +56,7 @@
     THREAD_MONITOR_SWITCH,
     
     VM_CAPABILITIES,
+    VM_DEADLOCK,
     TABLE,
     DETAILS,
     TIMELINE,
@@ -68,7 +69,10 @@
     THREAD_DUMP,
     
     MISSING_INFO,
-    THREAD_DETAILS_EMTPY;
+    THREAD_DETAILS_EMTPY,
+
+    CHECK_FOR_DEADLOCKS,
+    ;
 
     public static final String RESOURCE_BUNDLE =
             "com.redhat.thermostat.thread.client.common.locale.strings";
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java	Fri Jun 07 11:29:06 2013 -0400
@@ -83,5 +83,8 @@
     }
 
     public abstract void displayThreadDetails(ThreadTableBean thread);
+
+    public abstract VmDeadLockView createDeadLockView();
+
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/VmDeadLockView.java	Fri Jun 07 11:29:06 2013 -0400
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012, 2013 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.view;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+
+public abstract class VmDeadLockView extends BasicView {
+
+    public static enum VmDeadLockViewAction {
+        CHECK_FOR_DEADLOCK,
+        ;
+    }
+
+    protected final ActionNotifier<VmDeadLockViewAction> deadLockNotifier;
+
+    public VmDeadLockView() {
+        deadLockNotifier = new ActionNotifier<>(this);
+    }
+
+    public void addVmDeadLockViewActionListener(ActionListener<VmDeadLockViewAction> listener) {
+        deadLockNotifier.addActionListener(listener);
+    }
+
+    public void removeVmDeadLockViewActionListener(ActionListener<VmDeadLockViewAction> listener) {
+        deadLockNotifier.removeActionListener(listener);
+    }
+
+    public abstract void setDeadLockInformation(String info);
+}
--- a/thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties	Fri Jun 07 11:29:06 2013 -0400
@@ -16,6 +16,7 @@
 THREAD_MONITOR_SWITCH = Monitor Thread
 
 VM_CAPABILITIES = VM Capabilities
+VM_DEADLOCK = Deadlock Detection
 TABLE = Table
 DETAILS = Details
 TIMELINE = Timeline
@@ -28,3 +29,5 @@
 THREAD_DUMP = Thread Dump
 
 THREAD_DETAILS_EMTPY = Please double-click on a thread in the thread table
+
+CHECK_FOR_DEADLOCKS = Check
\ No newline at end of file
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/LocaleResources.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/LocaleResources.java	Fri Jun 07 11:29:06 2013 -0400
@@ -44,6 +44,8 @@
 
     WARNING_CANNOT_DISABLE,
     WARNING_CANNOT_ENABLE,
+
+    NO_DEADLOCK_DETECTED,
     ;
 
     static final String RESOURCE_BUNDLE = "com.redhat.thermostat.thread.client.controller.impl.strings";
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Fri Jun 07 11:29:06 2013 -0400
@@ -144,14 +144,18 @@
     }
     
     private void initControllers() {
+        TimerFactory tf = appService.getTimerFactory();
                 
         VMThreadCapabilitiesController capsController =
                 new VMThreadCapabilitiesController(view.createVMThreadCapabilitiesView(), collector);
         capsController.initialize();
         
+        VmDeadLockController deadLockController =
+                new VmDeadLockController(view.createDeadLockView(), collector, tf.createTimer());
+        deadLockController.initialize();
+
         ThreadTableView threadTableView = view.createThreadTableView();
         threadTableView.addThreadSelectionActionListener(new ThreadSelectionActionListener());
-        TimerFactory tf = appService.getTimerFactory();
         
         CommonController threadCountController =
                 new ThreadCountController(view.createThreadCountView(), collector, tf.createTimer());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VmDeadLockController.java	Fri Jun 07 11:29:06 2013 -0400
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012, 2013 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.controller.impl;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.swing.SwingUtilities;
+
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView.VmDeadLockViewAction;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
+
+public class VmDeadLockController {
+
+    private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer();
+
+    private VmDeadLockView view;
+    private ThreadCollector collector;
+    private Timer timer;
+
+    private final AtomicReference<String> descriptionRef =  new AtomicReference<>("");
+
+    public VmDeadLockController(VmDeadLockView view, ThreadCollector collector, Timer timer) {
+        this.view = view;
+        this.collector = collector;
+        this.timer = timer;
+    }
+
+    public void initialize() {
+        view.addVmDeadLockViewActionListener(new ActionListener<VmDeadLockViewAction>() {
+            @Override
+            public void actionPerformed(ActionEvent<VmDeadLockViewAction> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                case CHECK_FOR_DEADLOCK:
+                    checkForDeadLock();
+                    updateView();
+                    break;
+                default:
+                    break;
+                }
+            }
+        });
+
+        timer.setAction(new Runnable() {
+            @Override
+            public void run() {
+                checkStorageForDeadLockData();
+                updateView();
+            }
+        });
+        timer.setDelay(5);
+        timer.setInitialDelay(0);
+        timer.setTimeUnit(TimeUnit.SECONDS);
+        timer.setSchedulingType(SchedulingType.FIXED_DELAY);
+
+        view.addActionListener(new ActionListener<Action>() {
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                case HIDDEN:
+                    timer.stop();
+                    break;
+                case VISIBLE:
+                    timer.start();
+                    break;
+                }
+            }
+        });
+    }
+
+    private void checkForDeadLock() {
+        askAgentToCheckForDeadLock();
+        checkStorageForDeadLockData();
+    }
+
+    private void askAgentToCheckForDeadLock() {
+        collector.requestDeadLockCheck();
+    }
+
+    private void checkStorageForDeadLockData() {
+        VmDeadLockData data = collector.getLatestDeadLockData();
+        String description = data.getDeadLockDescription();
+        if (description.equals(VmDeadLockData.NO_DEADLOCK)) {
+            description = translate.localize(LocaleResources.NO_DEADLOCK_DETECTED).getContents();
+        }
+        this.descriptionRef.set(description);
+
+    }
+
+    private void updateView() {
+        view.setDeadLockInformation(descriptionRef.get());
+    }
+
+}
--- a/thread/client-controllers/src/main/resources/com/redhat/thermostat/thread/client/controller/impl/strings.properties	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-controllers/src/main/resources/com/redhat/thermostat/thread/client/controller/impl/strings.properties	Fri Jun 07 11:29:06 2013 -0400
@@ -1,3 +1,4 @@
 CONTROLLER_NAME = Threads
 WARNING_CANNOT_ENABLE = Cannot enable Thread recording
 WARNING_CANNOT_DISABLE = Cannot disable Thread recording
+NO_DEADLOCK_DETECTED = No Deadlocks Detected.
\ No newline at end of file
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java	Fri Jun 07 11:29:06 2013 -0400
@@ -69,6 +69,7 @@
 import com.redhat.thermostat.thread.client.common.view.ThreadView;
 import com.redhat.thermostat.thread.client.common.view.VMThreadCapabilitiesView;
 import com.redhat.thermostat.thread.client.common.view.ThreadTableView.ThreadSelectionAction;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
 
 public class ThreadInformationControllerTest {
 
@@ -85,6 +86,7 @@
 
     private ThreadTableView threadTableView;
     private VMThreadCapabilitiesView threadCapsView;
+    private VmDeadLockView deadLockView;
     private ThreadTimelineView threadTimelineView;
     private ThreadCountView threadCountView;
     
@@ -101,6 +103,7 @@
 
     private void setUpView() {
         threadCapsView = mock(VMThreadCapabilitiesView.class);
+        deadLockView = mock(VmDeadLockView.class);
         threadTableView = mock(ThreadTableView.class);
         threadTimelineView = mock(ThreadTimelineView.class);
         threadCountView = mock(ThreadCountView.class);
@@ -110,6 +113,7 @@
         when(viewFactory.createView()).thenReturn(view);
         
         when(view.createVMThreadCapabilitiesView()).thenReturn(threadCapsView);
+        when(view.createDeadLockView()).thenReturn(deadLockView);
         when(view.createThreadTableView()).thenReturn(threadTableView);
         when(view.createThreadTimelineView()).thenReturn(threadTimelineView);
         when(view.createThreadCountView()).thenReturn(threadCountView);
@@ -158,6 +162,7 @@
         
         verify(view).createThreadTableView();
         verify(view).createVMThreadCapabilitiesView();
+        verify(view).createDeadLockView();
         verify(view).createThreadTimelineView();
         verify(view).createThreadCountView();
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/VmDeadLockControllerTest.java	Fri Jun 07 11:29:06 2013 -0400
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2012, 2013 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.controller.impl;
+
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView.VmDeadLockViewAction;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
+
+public class VmDeadLockControllerTest {
+
+    private Timer timer;
+    private VmDeadLockView view;
+    private ThreadCollector collector;
+
+    private VmDeadLockController controller;
+
+    @Before
+    public void setUp() {
+        timer = mock(Timer.class);
+
+        view = mock(VmDeadLockView.class);
+
+        collector = mock(ThreadCollector.class);
+
+        controller = new VmDeadLockController(view, collector, timer);
+    }
+
+    @Test
+    public void verifyInitilizeRegistersActionListener() {
+        controller.initialize();
+
+        verify(view).addVmDeadLockViewActionListener(isA(ActionListener.class));
+    }
+
+    @Test
+    public void verifyRealDeadLockDataIsDisplayedOnViewAction() {
+        final String DESCRIPTION = "foo bar";
+        VmDeadLockData data = new VmDeadLockData();
+        data.setDeadLockDescription(DESCRIPTION);
+
+        controller.initialize();
+
+        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addVmDeadLockViewActionListener(listenerCaptor.capture());
+
+        ActionListener<VmDeadLockViewAction> listener = (ActionListener<VmDeadLockViewAction>) listenerCaptor.getValue();
+
+        when(collector.getLatestDeadLockData()).thenReturn(data);
+
+        listener.actionPerformed(new ActionEvent<VmDeadLockViewAction>(view, VmDeadLockViewAction.CHECK_FOR_DEADLOCK));
+
+        verify(collector).requestDeadLockCheck();
+        verify(view).setDeadLockInformation(DESCRIPTION);
+    }
+
+    @Test
+    public void verifyNoDeadLockDataIsDisplayedOnViewAction() {
+        VmDeadLockData data = new VmDeadLockData();
+        data.setDeadLockDescription(VmDeadLockData.NO_DEADLOCK);
+
+        controller.initialize();
+
+        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addVmDeadLockViewActionListener(listenerCaptor.capture());
+
+        ActionListener<VmDeadLockViewAction> listener = (ActionListener<VmDeadLockViewAction>) listenerCaptor.getValue();
+
+        when(collector.getLatestDeadLockData()).thenReturn(data);
+
+        listener.actionPerformed(new ActionEvent<VmDeadLockViewAction>(view, VmDeadLockViewAction.CHECK_FOR_DEADLOCK));
+
+        verify(collector).requestDeadLockCheck();
+        verify(view).setDeadLockInformation("No Deadlocks Detected.");
+    }
+
+    @Test
+    public void verifyInitializeSetsUpTimer() {
+        controller.initialize();
+
+        verify(timer).setAction(isA(Runnable.class));
+        verify(timer).setDelay(5);
+        verify(timer).setInitialDelay(0);
+        verify(timer).setSchedulingType(SchedulingType.FIXED_DELAY);
+        verify(timer).setTimeUnit(TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void verifyTimerIsEnabledWhenViewIsVisible() {
+        controller.initialize();
+
+        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
+
+        verify(view).addActionListener(listenerCaptor.capture());
+
+        ActionListener<BasicView.Action> visibilityListener = listenerCaptor.getValue();
+
+        visibilityListener.actionPerformed(new ActionEvent<BasicView.Action>(view, Action.VISIBLE));
+
+        verify(timer).start();
+
+        visibilityListener.actionPerformed(new ActionEvent<BasicView.Action>(view, Action.HIDDEN));
+
+        verify(timer).stop();
+    }
+
+    @Test
+    public void verifyTimerActionRefreshesView() {
+        doThrow(new AssertionError()).when(collector).requestDeadLockCheck();
+
+        VmDeadLockData data = new VmDeadLockData();
+        data.setDeadLockDescription(VmDeadLockData.NO_DEADLOCK);
+        controller.initialize();
+
+        when(collector.getLatestDeadLockData()).thenReturn(data);
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(timer).setAction(runnableCaptor.capture());
+
+        Runnable action = runnableCaptor.getValue();
+
+        action.run();
+
+        verify(view).setDeadLockInformation("No Deadlocks Detected.");
+    }
+}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java	Fri Jun 07 11:29:06 2013 -0400
@@ -61,6 +61,7 @@
 import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
 import com.redhat.thermostat.thread.client.common.view.ThreadView;
 import com.redhat.thermostat.thread.client.common.view.VMThreadCapabilitiesView;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
 
 public class SwingThreadView extends ThreadView implements SwingComponent {
     
@@ -71,6 +72,7 @@
     private SwingThreadCountView threadCountView;
     private SwingThreadTableView threadTableView;
     private SwingVMThreadCapabilitiesView vmCapsView;
+    private SwingVmDeadLockView vmDeadLockView;
     private SwingThreadTimelineView threadTimelineView;
     private SwingThreadDetailsView threadDetailsView;
 
@@ -171,6 +173,9 @@
         vmCapsView = new SwingVMThreadCapabilitiesView();
         bottomPane.addTab(t.localize(LocaleResources.VM_CAPABILITIES).getContents(), vmCapsView.getUiComponent());
 
+        vmDeadLockView = new SwingVmDeadLockView();
+        bottomPane.addTab(t.localize(LocaleResources.VM_DEADLOCK).getContents(), vmDeadLockView.getUiComponent());
+
         panel.getSplitPane().setBottomComponent(bottomPane);
     }
     
@@ -211,6 +216,11 @@
     public VMThreadCapabilitiesView createVMThreadCapabilitiesView() {
         return vmCapsView;
     }
+
+    @Override
+    public VmDeadLockView createDeadLockView() {
+        return vmDeadLockView;
+    }
     
     @Override
     public ThreadTableView createThreadTableView() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVmDeadLockView.java	Fri Jun 07 11:29:06 2013 -0400
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012, 2013 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.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+
+import com.redhat.thermostat.client.swing.ComponentVisibleListener;
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
+
+public class SwingVmDeadLockView extends VmDeadLockView implements SwingComponent {
+
+    private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer();
+
+    private final JPanel actualComponent = new JPanel();
+    private final JTextArea description = new JTextArea();
+
+    public SwingVmDeadLockView() {
+        actualComponent.setLayout(new GridBagLayout());
+
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridy = 0;
+        c.anchor = GridBagConstraints.LINE_END;
+        JButton recheckButton = new JButton(translate.localize(LocaleResources.CHECK_FOR_DEADLOCKS).getContents());
+        recheckButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                deadLockNotifier.fireAction(VmDeadLockViewAction.CHECK_FOR_DEADLOCK);
+            }
+        });
+
+        actualComponent.add(recheckButton, c);
+
+        c.anchor = GridBagConstraints.LINE_START;
+        c.gridy++;
+        c.fill = GridBagConstraints.BOTH;
+        c.weightx = 1;
+        c.weighty = 1;
+
+        JScrollPane scrollPane = new JScrollPane(description);
+        actualComponent.add(scrollPane, c);
+
+        actualComponent.addHierarchyListener(new ComponentVisibleListener() {
+            @Override
+            public void componentShown(Component component) {
+                notifier.fireAction(Action.VISIBLE);
+            }
+
+            @Override
+            public void componentHidden(Component component) {
+                notifier.fireAction(Action.VISIBLE);
+            }
+        });
+    }
+
+    @Override
+    public void setDeadLockInformation(final String info) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                description.setText(info);
+            }
+        });
+    }
+
+    @Override
+    public Component getUiComponent() {
+        return actualComponent;
+    }
+
+}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/HarvesterCommand.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/HarvesterCommand.java	Fri Jun 07 11:29:06 2013 -0400
@@ -40,6 +40,7 @@
 
     START,
     STOP,
+    FIND_DEADLOCKS,
     
     VM_ID;
 
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Fri Jun 07 11:29:06 2013 -0400
@@ -42,6 +42,7 @@
 import com.redhat.thermostat.storage.core.Key;
 import com.redhat.thermostat.storage.core.Storage;
 import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
 import com.redhat.thermostat.thread.model.ThreadInfoData;
 import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
 import com.redhat.thermostat.thread.model.ThreadSummary;
@@ -118,6 +119,15 @@
     
     void saveThreadInfo(ThreadInfoData info);
     List<ThreadInfoData> loadThreadInfo(VmRef ref, long since);
+
+    static final String DEADLOCK_DESCRIPTION = "description";
+    static final Key<String> DEADLOCK_DESCRIPTION_KEY = new Key<>(DEADLOCK_DESCRIPTION, false);
+    static final Category<VmDeadLockData> DEADLOCK_INFO = new Category<>("vm-deadlock-data", VmDeadLockData.class,
+            Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP,
+            DEADLOCK_DESCRIPTION_KEY);
+
+    void saveDeadLockStatus(VmDeadLockData deadLockInfo);
+    VmDeadLockData loadLatestDeadLockStatus(VmRef ref);
     
     Storage getStorage();
 }
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java	Fri Jun 07 11:29:06 2013 -0400
@@ -49,6 +49,7 @@
 import com.redhat.thermostat.storage.core.VmRef;
 import com.redhat.thermostat.storage.model.Pojo;
 import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
 import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
 import com.redhat.thermostat.thread.model.ThreadInfoData;
 import com.redhat.thermostat.thread.model.ThreadSummary;
@@ -56,13 +57,14 @@
 
 public class ThreadDaoImpl implements ThreadDao {
 
-    private Storage storage; 
+    private Storage storage;
     public ThreadDaoImpl(Storage storage) {
         this.storage = storage;
         storage.registerCategory(THREAD_CAPABILITIES);
         storage.registerCategory(THREAD_SUMMARY);
         storage.registerCategory(THREAD_HARVESTING_STATUS);
         storage.registerCategory(THREAD_INFO);
+        storage.registerCategory(DEADLOCK_INFO);
     }
 
     @Override
@@ -169,6 +171,28 @@
         
         return result;
     }
+
+    @Override
+    public VmDeadLockData loadLatestDeadLockStatus(VmRef ref) {
+        Query<VmDeadLockData> query = prepareQuery(DEADLOCK_INFO, ref);
+        query.sort(Key.TIMESTAMP, Query.SortDirection.DESCENDING);
+        query.limit(1);
+
+        Cursor<VmDeadLockData> cursor = query.execute();
+        if (cursor.hasNext()) {
+            VmDeadLockData data = cursor.next();
+            return data;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void saveDeadLockStatus(VmDeadLockData deadLockInfo) {
+        Put add = storage.createAdd(DEADLOCK_INFO);
+        add.setPojo(deadLockInfo);
+        add.apply();
+    }
     
     private <T extends Pojo> Query<T> prepareQuery(Category<T> category, VmRef vm) {
         return prepareQuery(category, vm.getIdString(), vm.getAgent().getAgentId());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/model/VmDeadLockData.java	Fri Jun 07 11:29:06 2013 -0400
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2012, 2013 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.model;
+
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.storage.model.TimeStampedPojo;
+
+@Entity
+public class VmDeadLockData extends BasePojo implements TimeStampedPojo {
+
+    /** Used as the description when no deadlock could be detected */
+    public static final String NO_DEADLOCK = "no-deadlocks";
+
+    private long timeStamp;
+    private int vmId;
+    private String description;
+
+    @Persist
+    @Override
+    public long getTimeStamp() {
+        return timeStamp;
+    }
+
+    @Persist
+    public void setTimeStamp(long timeStamp) {
+        this.timeStamp = timeStamp;
+    }
+
+    @Persist
+    public void setVmId(int vmId) {
+        this.vmId = vmId;
+    }
+
+    @Persist
+    public int getVmId() {
+        return vmId;
+    }
+
+    @Persist
+    public String getDeadLockDescription() {
+        return description;
+    }
+
+    @Persist
+    public void setDeadLockDescription(String description) {
+        this.description = description;
+    }
+
+}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java	Fri Jun 07 11:29:06 2013 -0400
@@ -64,6 +64,7 @@
 import com.redhat.thermostat.thread.dao.ThreadDao;
 import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
 import com.redhat.thermostat.thread.model.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
 
 public class ThreadDaoImplTest {
 
@@ -166,6 +167,55 @@
     }
 
     @Test
+    public void testLoadLatestDeadLockStatus() {
+        VmRef vm = mock(VmRef.class);
+        when(vm.getId()).thenReturn(42);
+        when(vm.getIdString()).thenReturn("42");
+
+        HostRef agent = mock(HostRef.class);
+        when(agent.getAgentId()).thenReturn("0xcafe");
+        when(vm.getAgent()).thenReturn(agent);
+
+        Storage storage = mock(Storage.class);
+        Query<VmDeadLockData> query = mock(Query.class);
+        Cursor<VmDeadLockData> cursor = mock(Cursor.class);
+        VmDeadLockData data = mock(VmDeadLockData.class);
+
+        when(cursor.hasNext()).thenReturn(true);
+        when(cursor.next()).thenReturn(data);
+        when(query.execute()).thenReturn(cursor);
+
+        when(storage.createQuery(ThreadDaoImpl.DEADLOCK_INFO)).thenReturn(query);
+
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        VmDeadLockData result = dao.loadLatestDeadLockStatus(vm);
+
+        assertSame(data, result);
+
+        verify(query).where(Key.AGENT_ID, Criteria.EQUALS, agent.getAgentId());
+        verify(query).where(Key.VM_ID, Criteria.EQUALS, vm.getId());
+        verify(query).sort(Key.TIMESTAMP, SortDirection.DESCENDING);
+        verify(query).execute();
+        verify(query).limit(1);
+        verifyNoMoreInteractions(query);
+    }
+
+    @Test
+    public void testSaveDeadLockStatus() {
+        Storage storage = mock(Storage.class);
+        Add add = mock(Add.class);
+        when(storage.createAdd(ThreadDaoImpl.DEADLOCK_INFO)).thenReturn(add);
+
+        VmDeadLockData status = mock(VmDeadLockData.class);
+
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        dao.saveDeadLockStatus(status);
+
+        verify(add).setPojo(status);
+        verify(add).apply();
+    }
+
+    @Test
     public void testGetLatestHarvestingStatus() {
         VmRef vm = mock(VmRef.class);
         when(vm.getId()).thenReturn(42);
--- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java	Fri Jun 07 11:29:06 2013 -0400
@@ -36,7 +36,6 @@
 
 package com.redhat.thermostat.thread.harvester;
 
-import java.io.IOException;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
@@ -50,12 +49,14 @@
 
 import javax.management.MalformedObjectNameException;
 
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.SystemClock;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.thread.dao.ThreadDao;
-
 import com.redhat.thermostat.thread.model.ThreadInfoData;
 import com.redhat.thermostat.thread.model.ThreadSummary;
 import com.redhat.thermostat.thread.model.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
 import com.redhat.thermostat.utils.management.MXBeanConnection;
 import com.redhat.thermostat.utils.management.MXBeanConnectionPool;
 
@@ -67,18 +68,25 @@
     private boolean isConnected;
     private ScheduledExecutorService threadPool;
     private ScheduledFuture<?> harvester;
+    private Clock clock;
     
     private MXBeanConnectionPool connectionPool;
     private MXBeanConnection connection;
     private ThreadMXBean collectorBean;
     private ThreadDao threadDao;
     private int vmId;
+
     
     Harvester(ThreadDao threadDao, ScheduledExecutorService threadPool, String vmId, MXBeanConnectionPool connectionPool) {
+        this(threadDao, threadPool, new SystemClock(), vmId, connectionPool);
+    }
+
+    Harvester(ThreadDao threadDao, ScheduledExecutorService threadPool, Clock clock, String vmId, MXBeanConnectionPool connectionPool) {
         this.threadDao = threadDao;
         this.vmId = Integer.valueOf(vmId);
         this.threadPool = threadPool;
         this.connectionPool = connectionPool;
+        this.clock = clock;
     }
     
     synchronized boolean start() {
@@ -86,6 +94,16 @@
             return true;
         }
 
+        if (!connect()) {
+            return false;
+        }
+
+        harvester = threadPool.scheduleAtFixedRate(new HarvesterAction(), 0, 250, TimeUnit.MILLISECONDS);
+
+        return isConnected;
+    }
+
+    private boolean connect() {
         try {
             connection = connectionPool.acquire(vmId);
         } catch (Exception ex) {
@@ -94,9 +112,7 @@
         }
         
         isConnected = true;
-        harvester = threadPool.scheduleAtFixedRate(new HarvesterAction(), 0, 250, TimeUnit.MILLISECONDS);
-        
-        return isConnected;
+        return true;
     }
     
     boolean isConnected() {
@@ -110,6 +126,10 @@
         
         harvester.cancel(false);
         
+        return disconnect();
+    }
+
+    private boolean disconnect() {
         if (collectorBean != null) {
             collectorBean = null;
         }
@@ -144,7 +164,7 @@
     
     synchronized void harvestData() {
       try {
-          long timestamp = System.currentTimeMillis();
+          long timestamp = clock.getRealTimeMillis();
           
           ThreadSummary summary = new ThreadSummary();
           
@@ -257,5 +277,50 @@
 
         return success;
     }
+
+    public boolean saveDeadLockData() {
+        boolean disconnectAtEnd = false;
+        if (!isConnected) {
+            disconnectAtEnd = true;
+
+            connect();
+        }
+
+        try {
+            if (collectorBean == null) {
+                collectorBean = getDataCollectorBean(connection);
+            }
+
+            String description = null;
+            long timeStamp = clock.getRealTimeMillis();
+            long[] ids = collectorBean.findDeadlockedThreads();
+            if (ids == null) {
+                description = VmDeadLockData.NO_DEADLOCK;
+            } else {
+                ThreadInfo[] infos = collectorBean.getThreadInfo(ids, true, true);
+                StringBuilder descriptionBuilder = new StringBuilder();
+                for (ThreadInfo info : infos) {
+                    descriptionBuilder.append(info.toString()).append("\n");
+                }
+                description = descriptionBuilder.toString();
+            }
+
+            VmDeadLockData data = new VmDeadLockData();
+            data.setTimeStamp(timeStamp);
+            data.setVmId(vmId);
+            data.setDeadLockDescription(description);
+
+            threadDao.saveDeadLockStatus(data);
+
+        } catch (MalformedObjectNameException e) {
+            e.printStackTrace();
+        }
+
+        if (disconnectAtEnd) {
+            disconnect();
+        }
+
+        return true;
+    }
 }
 
--- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java	Fri Jun 07 11:29:06 2013 -0400
@@ -114,6 +114,10 @@
             result = stopHarvester(vmId);
             break;
         }
+        case FIND_DEADLOCKS:
+            String vmId = request.getParameter(HarvesterCommand.VM_ID.name());
+            result = findAndSaveDeadLockInformation(vmId);
+            break;
         default:
             result = false;
             break;
@@ -213,5 +217,10 @@
         return dao != null;
     }
 
+    public boolean findAndSaveDeadLockInformation(String vmId) {
+        Harvester harvester = getHarvester(vmId);
+        boolean result = harvester.saveDeadLockData();
+        return result;
+    }
 }
 
--- a/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java	Fri Jun 07 11:29:06 2013 -0400
@@ -60,10 +60,12 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
+import com.redhat.thermostat.common.Clock;
 import com.redhat.thermostat.thread.dao.ThreadDao;
 import com.redhat.thermostat.thread.model.ThreadInfoData;
 import com.redhat.thermostat.thread.model.ThreadSummary;
 import com.redhat.thermostat.thread.model.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
 import com.redhat.thermostat.utils.management.MXBeanConnection;
 import com.redhat.thermostat.utils.management.MXBeanConnectionPool;
 
@@ -363,5 +365,47 @@
         assertEquals(ThreadDao.CPU_TIME, features[0]);
         assertEquals(ThreadDao.CONTENTION_MONITOR, features[1]);
     }    
+
+    @Test
+    public void testCheckForDeadLocks() {
+        MXBeanConnectionPool pool = mock(MXBeanConnectionPool.class);
+
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+
+        ArgumentCaptor<VmDeadLockData> deadLockCapture = ArgumentCaptor.forClass(VmDeadLockData.class);
+
+        ThreadDao dao = mock(ThreadDao.class);
+
+        ThreadInfo[] threadInfo = new ThreadInfo[0];
+
+        final ThreadMXBean collectorBean = mock(ThreadMXBean.class);
+        when(collectorBean.findDeadlockedThreads()).thenReturn(new long[] { -1, 0, 1 });
+        when(collectorBean.getThreadInfo(new long[] { -1, 0, 1 }, true, true)).thenReturn(threadInfo);
+
+        Clock clock = mock(Clock.class);
+        when(clock.getRealTimeMillis()).thenReturn(101010l);
+
+        final boolean[] getDataCollectorBeanCalled = new boolean[1];
+
+        Harvester harvester = new Harvester(dao, executor, clock, "42", pool) {
+            @Override
+            ThreadMXBean getDataCollectorBean(MXBeanConnection connection)
+                    throws MalformedObjectNameException {
+                getDataCollectorBeanCalled[0] = true;
+                return collectorBean;
+            }
+        };
+
+        harvester.saveDeadLockData();
+        assertTrue(getDataCollectorBeanCalled[0]);
+
+        verify(dao).saveDeadLockStatus(deadLockCapture.capture());
+
+        VmDeadLockData data = deadLockCapture.getValue();
+
+        assertEquals(42, data.getVmId());
+        assertEquals(101010l, data.getTimeStamp());
+        assertEquals("", data.getDeadLockDescription());
+    }
 }
 
--- a/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java	Fri Jun 07 10:54:37 2013 +0200
+++ b/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java	Fri Jun 07 11:29:06 2013 -0400
@@ -138,6 +138,46 @@
     }
     
     @Test
+    public void testFindDeadLocks() {
+        ThreadDao dao = mock(ThreadDao.class);
+        Request request = mock(Request.class);
+
+        final boolean[] createHarvesterCalled = new boolean[1];
+        final Harvester harverster = mock(Harvester.class);
+        when(harverster.start()).thenReturn(true);
+
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+
+        when(request.getParameter(captor.capture())).
+                thenReturn(HarvesterCommand.FIND_DEADLOCKS.name()).
+                thenReturn("42").
+                thenReturn("0xcafe");
+
+        ThreadHarvester threadHarvester = new ThreadHarvester(executor, pool) {
+            @Override
+            Harvester createHarvester(String vmId) {
+
+                createHarvesterCalled[0] = true;
+                assertEquals("42", vmId);
+
+                return harverster;
+            }
+        };
+        threadHarvester.setThreadDao(dao);
+        threadHarvester.receive(request);
+
+        List<String> values = captor.getAllValues();
+        assertEquals(2, values.size());
+
+        assertEquals(HarvesterCommand.class.getName(), values.get(0));
+        assertEquals(HarvesterCommand.VM_ID.name(), values.get(1));
+
+        assertTrue(createHarvesterCalled[0]);
+
+        verify(harverster).saveDeadLockData();
+    }
+
+    @Test
     public void testSaveVmCaps() {
         ThreadDao dao = mock(ThreadDao.class);