# HG changeset patch # User Omair Majid # Date 1370618946 14400 # Node ID f747a057d7a192eec487ba0f3fc538df2897ea36 # Parent 3a2501017e913fdc091878dfe5ed9e0a923b17c8 Add back checking for deadlocks Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-May/006883.html diff -r 3a2501017e91 -r f747a057d7a1 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/ThreadCollector.java --- 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 getThreadInfo(); + + /** + * Check for deadlocks. {@link #getLatestDeadLockData} needs to be called to + * obtain the data + */ + void requestDeadLockCheck(); + + /** Return the latest deadlock data */ + VmDeadLockData getLatestDeadLockData(); } diff -r 3a2501017e91 -r f747a057d7a1 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadMXBeanCollector.java --- 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()); diff -r 3a2501017e91 -r f747a057d7a1 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java --- 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"; diff -r 3a2501017e91 -r f747a057d7a1 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java --- 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(); + } diff -r 3a2501017e91 -r f747a057d7a1 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/VmDeadLockView.java --- /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 + * . + * + * 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 deadLockNotifier; + + public VmDeadLockView() { + deadLockNotifier = new ActionNotifier<>(this); + } + + public void addVmDeadLockViewActionListener(ActionListener listener) { + deadLockNotifier.addActionListener(listener); + } + + public void removeVmDeadLockViewActionListener(ActionListener listener) { + deadLockNotifier.removeActionListener(listener); + } + + public abstract void setDeadLockInformation(String info); +} diff -r 3a2501017e91 -r f747a057d7a1 thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties --- 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 diff -r 3a2501017e91 -r f747a057d7a1 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/LocaleResources.java --- 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"; diff -r 3a2501017e91 -r f747a057d7a1 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java --- 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()); diff -r 3a2501017e91 -r f747a057d7a1 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VmDeadLockController.java --- /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 + * . + * + * 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 translate = LocaleResources.createLocalizer(); + + private VmDeadLockView view; + private ThreadCollector collector; + private Timer timer; + + private final AtomicReference 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() { + @Override + public void actionPerformed(ActionEvent 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() { + @Override + public void actionPerformed(ActionEvent 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()); + } + +} diff -r 3a2501017e91 -r f747a057d7a1 thread/client-controllers/src/main/resources/com/redhat/thermostat/thread/client/controller/impl/strings.properties --- 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 diff -r 3a2501017e91 -r f747a057d7a1 thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java --- 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(); } diff -r 3a2501017e91 -r f747a057d7a1 thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/VmDeadLockControllerTest.java --- /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 + * . + * + * 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 listenerCaptor = (ArgumentCaptor) ArgumentCaptor.forClass(ActionListener.class); + verify(view).addVmDeadLockViewActionListener(listenerCaptor.capture()); + + ActionListener listener = (ActionListener) listenerCaptor.getValue(); + + when(collector.getLatestDeadLockData()).thenReturn(data); + + listener.actionPerformed(new ActionEvent(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 listenerCaptor = (ArgumentCaptor) ArgumentCaptor.forClass(ActionListener.class); + verify(view).addVmDeadLockViewActionListener(listenerCaptor.capture()); + + ActionListener listener = (ActionListener) listenerCaptor.getValue(); + + when(collector.getLatestDeadLockData()).thenReturn(data); + + listener.actionPerformed(new ActionEvent(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 listenerCaptor = (ArgumentCaptor) ArgumentCaptor.forClass(ActionListener.class); + + verify(view).addActionListener(listenerCaptor.capture()); + + ActionListener visibilityListener = listenerCaptor.getValue(); + + visibilityListener.actionPerformed(new ActionEvent(view, Action.VISIBLE)); + + verify(timer).start(); + + visibilityListener.actionPerformed(new ActionEvent(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 runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(timer).setAction(runnableCaptor.capture()); + + Runnable action = runnableCaptor.getValue(); + + action.run(); + + verify(view).setDeadLockInformation("No Deadlocks Detected."); + } +} diff -r 3a2501017e91 -r f747a057d7a1 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java --- 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() { diff -r 3a2501017e91 -r f747a057d7a1 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVmDeadLockView.java --- /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 + * . + * + * 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 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; + } + +} diff -r 3a2501017e91 -r f747a057d7a1 thread/collector/src/main/java/com/redhat/thermostat/thread/collector/HarvesterCommand.java --- 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; diff -r 3a2501017e91 -r f747a057d7a1 thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java --- 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 loadThreadInfo(VmRef ref, long since); + + static final String DEADLOCK_DESCRIPTION = "description"; + static final Key DEADLOCK_DESCRIPTION_KEY = new Key<>(DEADLOCK_DESCRIPTION, false); + static final Category 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(); } diff -r 3a2501017e91 -r f747a057d7a1 thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java --- 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 query = prepareQuery(DEADLOCK_INFO, ref); + query.sort(Key.TIMESTAMP, Query.SortDirection.DESCENDING); + query.limit(1); + + Cursor 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 Query prepareQuery(Category category, VmRef vm) { return prepareQuery(category, vm.getIdString(), vm.getAgent().getAgentId()); diff -r 3a2501017e91 -r f747a057d7a1 thread/collector/src/main/java/com/redhat/thermostat/thread/model/VmDeadLockData.java --- /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 + * . + * + * 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; + } + +} diff -r 3a2501017e91 -r f747a057d7a1 thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java --- 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 query = mock(Query.class); + Cursor 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); diff -r 3a2501017e91 -r f747a057d7a1 thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java --- 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; + } } diff -r 3a2501017e91 -r f747a057d7a1 thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java --- 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; + } } diff -r 3a2501017e91 -r f747a057d7a1 thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java --- 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 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()); + } } diff -r 3a2501017e91 -r f747a057d7a1 thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java --- 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 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 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);