# HG changeset patch # User Roman Kennke # Date 1348093983 -7200 # Node ID 5e8550c83f37f7a9999e46521efbb4889d2265a1 # Parent 9eed9fa986ad001795b9b01ef254fb89b99ea2a5# Parent fda64c331b672f6a15c9b3144d2ce93028871d11 Merge diff -r 9eed9fa986ad -r 5e8550c83f37 client/command/src/main/java/com/redhat/thermostat/client/command/internal/RequestEncoder.java --- a/client/command/src/main/java/com/redhat/thermostat/client/command/internal/RequestEncoder.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/command/src/main/java/com/redhat/thermostat/client/command/internal/RequestEncoder.java Thu Sep 20 00:33:03 2012 +0200 @@ -36,22 +36,27 @@ package com.redhat.thermostat.client.command.internal; +import static org.jboss.netty.buffer.ChannelBuffers.dynamicBuffer; +import static org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer; + import java.util.Collection; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import com.redhat.thermostat.common.command.EncodingHelper; import com.redhat.thermostat.common.command.MessageEncoder; import com.redhat.thermostat.common.command.Request; -import static org.jboss.netty.buffer.ChannelBuffers.dynamicBuffer; -import static org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer; - class RequestEncoder extends MessageEncoder { + private static final Logger logger = Logger.getLogger(RequestEncoder.class.getName()); + @Override public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) { @@ -75,5 +80,12 @@ ChannelBuffer buf = wrappedBuffer(typeBuffer, parmsBuffer); Channels.write(ctx, e.getFuture(), buf); } - + + // This must be implemented, even though we are simply passing on the exception. If + // not implemented, this exception ends up going uncaught which causes problems. + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log(Level.WARNING, "Forwarding exception ", e.getCause()); + Channels.fireExceptionCaught(ctx, e.getCause()); + } } \ No newline at end of file diff -r 9eed9fa986ad -r 5e8550c83f37 client/command/src/main/java/com/redhat/thermostat/client/command/internal/RequestQueueImpl.java --- a/client/command/src/main/java/com/redhat/thermostat/client/command/internal/RequestQueueImpl.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/command/src/main/java/com/redhat/thermostat/client/command/internal/RequestQueueImpl.java Thu Sep 20 00:33:03 2012 +0200 @@ -45,6 +45,9 @@ import com.redhat.thermostat.client.command.RequestQueue; import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; class RequestQueueImpl implements RequestQueue { @@ -103,9 +106,17 @@ } ChannelFuture f = ((ClientBootstrap) ctx.getBootstrap()).connect(request.getTarget()); f.awaitUninterruptibly(); - Channel c = f.getChannel(); - c.getPipeline().addLast("responseHandler", new ResponseHandler(request)); - c.write(request); + if (f.isSuccess()) { + Channel c = f.getChannel(); + c.getPipeline().addLast("responseHandler", new ResponseHandler(request)); + c.write(request); + } else { + Response response = new Response(ResponseType.ERROR); + // TODO add more information once Response supports parameters. + for (RequestResponseListener listener : request.getListeners()) { + listener.fireComplete(request, response); + } + } } } } diff -r 9eed9fa986ad -r 5e8550c83f37 client/command/src/test/java/com/redhat/thermostat/client/command/internal/RequestEncoderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/command/src/test/java/com/redhat/thermostat/client/command/internal/RequestEncoderTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.command.internal; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Matchers.isA; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ExceptionEvent; +import org.junit.Test; + +public class RequestEncoderTest { + + /** + * This test verifies that exception events are forwarded upstream. This is to ensure that + * fireComplete() events get delivered in case of Exceptions. + * + * @throws Exception + */ + @Test + public void exceptionCaughtCallsFireComplete() throws Exception { + RequestEncoder encoder = new RequestEncoder(); + ChannelHandlerContext ctx = mock(ChannelHandlerContext.class); + ExceptionEvent evt = mock(ExceptionEvent.class); + when(evt.getCause()).thenReturn(new Exception()); + Channel channel = mock(Channel.class); + when(ctx.getChannel()).thenReturn(channel); + encoder.exceptionCaught(ctx, evt); + // Channels.fireExceptionCaught implicitly calls this + verify(ctx).sendUpstream(isA(ExceptionEvent.class)); + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 client/command/src/test/java/com/redhat/thermostat/client/command/internal/ResponseHandlerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/command/src/test/java/com/redhat/thermostat/client/command/internal/ResponseHandlerTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.command.internal; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.junit.Test; + +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; + +public class ResponseHandlerTest { + + @Test + public void messageReceivedCallsFireComplete() throws Exception { + Request req = mock(Request.class); + List listeners = new ArrayList<>(); + ResponseListenerFixture fixture = new ResponseListenerFixture(); + listeners.add(fixture); + when(req.getListeners()).thenReturn(listeners); + ResponseHandler handler = new ResponseHandler(req); + ChannelHandlerContext ctxt = mock(ChannelHandlerContext.class); + ChannelPipeline pipeline = mock(ChannelPipeline.class); + when(ctxt.getPipeline()).thenReturn(pipeline); + Channel channel = mock(Channel.class); + MessageEvent e = mock(MessageEvent.class); + when(e.getChannel()).thenReturn(channel); + Response response = mock(Response.class); + when(response.getType()).thenReturn(ResponseType.OK); + when(e.getMessage()).thenReturn(response); + + handler.messageReceived(ctxt, e); + assertTrue(fixture.isCalled()); + } + + @Test + public void exceptionCaughtCallsFireComplete() throws Exception { + Request req = mock(Request.class); + List listeners = new ArrayList<>(); + ResponseListenerFixture fixture = new ResponseListenerFixture(); + listeners.add(fixture); + when(req.getListeners()).thenReturn(listeners); + ResponseHandler handler = new ResponseHandler(req); + handler.exceptionCaught(mock(ChannelHandlerContext.class), mock(ExceptionEvent.class)); + assertTrue(fixture.isCalled()); + } + + private class ResponseListenerFixture implements RequestResponseListener { + + public boolean called = false; + + public boolean isCalled() { + return called; + } + + @Override + public void fireComplete(Request request, Response response) { + called = true; + } + + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/Activator.java --- a/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/Activator.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/Activator.java Thu Sep 20 00:33:03 2012 +0200 @@ -45,7 +45,7 @@ @Override public void start(BundleContext context) throws Exception { - context.registerService(VMContextAction.class.getName(), new KillVMAction(), null); + context.registerService(VMContextAction.class.getName(), new KillVMAction(new SwingVMKilledListener()), null); } @Override diff -r 9eed9fa986ad -r 5e8550c83f37 client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/KillVMAction.java --- a/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/KillVMAction.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/KillVMAction.java Thu Sep 20 00:33:03 2012 +0200 @@ -45,6 +45,7 @@ import com.redhat.thermostat.common.appctx.ApplicationContext; import com.redhat.thermostat.common.command.Request; import com.redhat.thermostat.common.command.Request.RequestType; +import com.redhat.thermostat.common.command.RequestResponseListener; import com.redhat.thermostat.common.dao.DAOFactory; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.common.locale.Translate; @@ -60,10 +61,15 @@ private static final String RECEIVER = "com.redhat.thermostat.agent.killvm.internal.KillVmReceiver"; private final DAOFactory dao; private final Translate t; + private final RequestResponseListener listener; - public KillVMAction() { + public KillVMAction(RequestResponseListener listener) { + if (listener == null) { + throw new IllegalArgumentException("Listener can't be null"); + } this.dao = ApplicationContext.getInstance().getDAOFactory(); this.t = LocaleResources.createLocalizer(); + this.listener = listener; } @Override @@ -85,7 +91,7 @@ Request murderer = getKillRequest(target); murderer.setParameter("vm-id", reference.getIdString()); murderer.setReceiver(RECEIVER); - murderer.addListener(new VMKilledListener()); + murderer.addListener(listener); RequestQueue queue = OSGIUtils.getInstance().getService(RequestQueue.class); queue.putRequest(murderer); diff -r 9eed9fa986ad -r 5e8550c83f37 client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/SwingVMKilledListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/SwingVMKilledListener.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,102 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.killvm.internal; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.client.killvm.locale.LocaleResources; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.locale.Translate; + +public class SwingVMKilledListener implements RequestResponseListener { + + private final Translate t; + private static final Logger logger = Logger + .getLogger(SwingVMKilledListener.class.getName()); + + public SwingVMKilledListener() { + this.t = LocaleResources.createLocalizer(); + } + + @Override + public void fireComplete(Request request, Response response) { + switch (response.getType()) { + case EXCEPTION: + logger.log(Level.SEVERE, + "Exception response from kill VM request. Command channel failure?"); + showErrorMessage(t.localize(LocaleResources.KILL_ACTION_EXCEPTION_RESPONSE_MSG)); + break; + case ERROR: + String vmId = request.getParameter("vm-id"); + logger.log(Level.SEVERE, + "Kill request error for VM ID " + + vmId); + showErrorMessage(t.localize(LocaleResources.KILL_ACTION_ERROR_RESPONSE_MSG, vmId)); + break; + case PONG: // fall-through, also OK :) + case OK: + logger.log(Level.INFO, + "VM with id " + request.getParameter("vm-id") + + " killed on host " + + request.getTarget().toString()); + break; + default: + logger.log(Level.WARNING, "Unknown result from KILL VM command."); + break; + } + } + + /* + * protected for testing + * + */ + protected void showErrorMessage(final String message) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(null, message, "", + JOptionPane.ERROR_MESSAGE); + } + }); + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/VMKilledListener.java --- a/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/internal/VMKilledListener.java Thu Sep 20 00:28:12 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * Copyright 2012 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.client.killvm.internal; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.redhat.thermostat.common.command.Request; -import com.redhat.thermostat.common.command.RequestResponseListener; -import com.redhat.thermostat.common.command.Response; - -public class VMKilledListener implements RequestResponseListener { - - private static final Logger logger = Logger - .getLogger(VMKilledListener.class.getName()); - - @Override - public void fireComplete(Request request, Response response) { - switch (response.getType()) { - case EXCEPTION: - logger.log(Level.SEVERE, - "Exception response from kill VM request. Command channel failure?"); - break; - case ERROR: - logger.log(Level.SEVERE, - "Kill request error for VM ID " - + request.getParameter("vm-id")); - break; - case PONG: // fall-through, also OK :) - case OK: - // TODO: Report this to user somehow (notification?) - logger.log(Level.INFO, - "VM with id " + request.getParameter("vm-id") - + " killed on host " - + request.getTarget().toString()); - break; - default: - logger.log(Level.WARNING, "Unknown result from KILL VM command."); - break; - } - } -} diff -r 9eed9fa986ad -r 5e8550c83f37 client/killvm/src/main/java/com/redhat/thermostat/client/killvm/locale/LocaleResources.java --- a/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/locale/LocaleResources.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/locale/LocaleResources.java Thu Sep 20 00:33:03 2012 +0200 @@ -42,6 +42,8 @@ ACTION_NAME, ACTION_DESCRIPTION, + KILL_ACTION_EXCEPTION_RESPONSE_MSG, + KILL_ACTION_ERROR_RESPONSE_MSG, MISSING_INFO; public static final String RESOURCE_BUNDLE = diff -r 9eed9fa986ad -r 5e8550c83f37 client/killvm/src/main/resources/com/redhat/thermostat/client/killvm/locale/strings.properties --- a/client/killvm/src/main/resources/com/redhat/thermostat/client/killvm/locale/strings.properties Thu Sep 20 00:28:12 2012 +0200 +++ b/client/killvm/src/main/resources/com/redhat/thermostat/client/killvm/locale/strings.properties Thu Sep 20 00:33:03 2012 +0200 @@ -1,3 +1,5 @@ ACTION_NAME = Kill Application ACTION_DESCRIPTION = Kill the selected VM Process -MISSING_INFO = Missing Information \ No newline at end of file +MISSING_INFO = Missing Information +KILL_ACTION_EXCEPTION_RESPONSE_MSG = Channel Transport Error. Please check your configuration! +KILL_ACTION_ERROR_RESPONSE_MSG = Failed to kill VM with pid {0} \ No newline at end of file diff -r 9eed9fa986ad -r 5e8550c83f37 client/killvm/src/test/java/com/redhat/thermostat/client/killvm/internal/KillVMActionTest.java --- a/client/killvm/src/test/java/com/redhat/thermostat/client/killvm/internal/KillVMActionTest.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/killvm/src/test/java/com/redhat/thermostat/client/killvm/internal/KillVMActionTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -83,7 +83,7 @@ ApplicationContextUtil.resetApplicationContext(); factory = mock(DAOFactory.class); ApplicationContext.getInstance().setDAOFactory(factory); - action = new KillVMAction(); + action = new KillVMAction(new SwingVMKilledListener()); } @After @@ -124,7 +124,7 @@ when(factory.getAgentInfoDAO()).thenReturn(agentDao); final Request req = mock(Request.class); - KillVMAction action = new KillVMAction() { + KillVMAction action = new KillVMAction(new SwingVMKilledListener()) { @Override Request getKillRequest(InetSocketAddress target) { return req; @@ -140,7 +140,7 @@ .forClass(String.class); verify(req).setParameter(vmIdParamCaptor.capture(), any(String.class)); assertEquals("vm-id", vmIdParamCaptor.getValue()); - verify(req).addListener(isA(VMKilledListener.class)); + verify(req).addListener(isA(SwingVMKilledListener.class)); ArgumentCaptor receiverCaptor = ArgumentCaptor .forClass(String.class); verify(req).setReceiver(receiverCaptor.capture()); diff -r 9eed9fa986ad -r 5e8550c83f37 client/killvm/src/test/java/com/redhat/thermostat/client/killvm/internal/SwingVMKilledListenerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/killvm/src/test/java/com/redhat/thermostat/client/killvm/internal/SwingVMKilledListenerTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,97 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.killvm.internal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.SocketAddress; + +import org.junit.Test; + +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; + +public class SwingVMKilledListenerTest { + + @Test + public void exceptionShowsError() { + ResponseActionListener listener = new ResponseActionListener(); + Request request = mock(Request.class); + Response resp = new Response(ResponseType.EXCEPTION); + listener.fireComplete(request, resp); + assertTrue(listener.isActionPerformed()); + } + + @Test + public void errorShowsError() { + ResponseActionListener listener = new ResponseActionListener(); + Request request = mock(Request.class); + Response resp = new Response(ResponseType.ERROR); + listener.fireComplete(request, resp); + assertTrue(listener.isActionPerformed()); + } + + @Test + public void okShowsNoMessage() { + ResponseActionListener listener = new ResponseActionListener(); + Request request = mock(Request.class); + SocketAddress addr = mock(SocketAddress.class); + when(request.getTarget()).thenReturn(addr); + Response resp = new Response(ResponseType.OK); + listener.fireComplete(request, resp); + assertFalse(listener.isActionPerformed()); + } + + private class ResponseActionListener extends SwingVMKilledListener { + + private boolean actionPerformed = false; + + public boolean isActionPerformed() { + return actionPerformed; + } + + @Override + protected void showErrorMessage(final String message) { + actionPerformed = true; + } + + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 client/swing-components/src/main/java/com/redhat/thermostat/swing/ChartPanel.java --- a/client/swing-components/src/main/java/com/redhat/thermostat/swing/ChartPanel.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/swing-components/src/main/java/com/redhat/thermostat/swing/ChartPanel.java Thu Sep 20 00:33:03 2012 +0200 @@ -51,7 +51,7 @@ private ChartRenderingInfo info; public ChartPanel(JFreeChart chart) { - this.chart = chart; + this(chart, new ChartRenderingInfo()); } public ChartPanel(JFreeChart chart, ChartRenderingInfo info) { diff -r 9eed9fa986ad -r 5e8550c83f37 client/swing-components/src/main/java/com/redhat/thermostat/swing/GradientRoundBorder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing-components/src/main/java/com/redhat/thermostat/swing/GradientRoundBorder.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,74 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.swing; + +import java.awt.Color; +import java.awt.Component; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Shape; +import java.io.Serializable; + +import javax.swing.UIManager; +import javax.swing.border.AbstractBorder; +import javax.swing.plaf.UIResource; + +@SuppressWarnings("serial") +public class GradientRoundBorder extends AbstractBorder implements UIResource, Serializable { + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + GraphicsUtils utils = GraphicsUtils.getInstance(); + Graphics2D graphics = utils.createAAGraphics(g); + + Color highlight = UIManager.getColor("textHighlight"); + if (highlight == null) { + highlight = Palette.EGYPTIAN_BLUE.getColor(); + } + Paint paint = new GradientPaint(x, y, highlight, 0, height, c.getBackground()); + graphics.setPaint(paint); + + graphics.translate(x, y); + + Shape shape = utils.getRoundShape(width, height); + graphics.draw(shape); + + graphics.dispose(); + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 client/swing-components/src/main/java/com/redhat/thermostat/swing/GraphicsUtils.java --- a/client/swing-components/src/main/java/com/redhat/thermostat/swing/GraphicsUtils.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/swing-components/src/main/java/com/redhat/thermostat/swing/GraphicsUtils.java Thu Sep 20 00:33:03 2012 +0200 @@ -85,7 +85,7 @@ } public Shape getRoundShape(int width, int height) { - return new RoundRectangle2D.Double(0, 0, width - 2, height - 1, 4, 4); + return new RoundRectangle2D.Double(0, 0, width, height, 4, 4); } public void setGradientPaint(Graphics2D g, int x, int height, Color start, Color stop) { diff -r 9eed9fa986ad -r 5e8550c83f37 client/swing-components/src/main/java/com/redhat/thermostat/swing/HeaderPanel.java --- a/client/swing-components/src/main/java/com/redhat/thermostat/swing/HeaderPanel.java Thu Sep 20 00:28:12 2012 +0200 +++ b/client/swing-components/src/main/java/com/redhat/thermostat/swing/HeaderPanel.java Thu Sep 20 00:33:03 2012 +0200 @@ -43,8 +43,6 @@ import java.lang.reflect.InvocationTargetException; import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; diff -r 9eed9fa986ad -r 5e8550c83f37 client/swing-components/src/main/java/com/redhat/thermostat/swing/models/LongRange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing-components/src/main/java/com/redhat/thermostat/swing/models/LongRange.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.swing.models; + +public class LongRange { + + long min; + long max; + + public void setMax(long max) { + this.max = max; + } + + public long getMax() { + return max; + } + + public void setMin(long min) { + this.min = min; + } + + + public long getMin() { + return min; + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 client/swing-components/src/main/java/com/redhat/thermostat/swing/models/LongRangeNormalizer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing-components/src/main/java/com/redhat/thermostat/swing/models/LongRangeNormalizer.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,81 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.swing.models; + +public class LongRangeNormalizer { + + private long minNormalized; + + private long maxNormalized; + + private long value; + + private LongRange range; + + public LongRangeNormalizer(LongRange range) { + this.range = range; + } + + public void setMaxNormalized(long maxNormalized) { + this.maxNormalized = maxNormalized; + } + + public void setMinNormalized(long minNormalized) { + this.minNormalized = minNormalized; + } + + public long getValue() { + return value; + } + + public void setValue(long newValue) { + this.value = newValue; + } + + public long getMaxNormalized() { + return maxNormalized; + } + + public long getMinNormalized() { + return minNormalized; + } + + public long getValueNormalized() { + double normalized = ((value - range.min) * (double)(maxNormalized - minNormalized)/(range.max - range.min)) + minNormalized; + return Math.round(normalized); + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 client/swing-components/src/test/java/com/redhat/thermostat/swing/models/LongRangeNormalizerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing-components/src/test/java/com/redhat/thermostat/swing/models/LongRangeNormalizerTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,120 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.swing.models; + +import static org.junit.Assert.*; +import junit.framework.Assert; + +import org.junit.Test; + +public class LongRangeNormalizerTest { + + @Test + public void testSameRange() { + + LongRange range = new LongRange(); + range.setMax(10); + range.setMin(0); + + LongRangeNormalizer model = new LongRangeNormalizer(range); + + model.setValue(5); + + model.setMaxNormalized(10); + model.setMinNormalized(0); + + + Assert.assertEquals((int) model.getValue(), model.getValueNormalized()); + } + + @Test + public void testDoubleRange() { + + LongRange range = new LongRange(); + range.setMax(10); + range.setMin(0); + + LongRangeNormalizer model = new LongRangeNormalizer(range); + + model.setValue(5); + + model.setMaxNormalized(20); + model.setMinNormalized(0); + + + Assert.assertEquals(10, model.getValueNormalized()); + } + + @Test + public void testRanges() { + + LongRange range = new LongRange(); + range.setMax(10); + range.setMin(0); + + LongRangeNormalizer model = new LongRangeNormalizer(range); + + model.setValue(5); + + model.setMaxNormalized(40); + model.setMinNormalized(0); + + Assert.assertEquals(20, model.getValueNormalized()); + + model.setMaxNormalized(60); + model.setMinNormalized(0); + + Assert.assertEquals(30, model.getValueNormalized()); + + model.setMaxNormalized(200); + model.setMinNormalized(100); + + Assert.assertEquals(150, model.getValueNormalized()); + + range.setMax(100); + range.setMin(0); + model.setValue(50); + + model.setMaxNormalized(1); + model.setMinNormalized(0); + + Assert.assertEquals(1, model.getValueNormalized()); + + model.setValue(49); + Assert.assertEquals(0, model.getValueNormalized()); + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 distribution/config/osgi-export.properties --- a/distribution/config/osgi-export.properties Thu Sep 20 00:28:12 2012 +0200 +++ b/distribution/config/osgi-export.properties Thu Sep 20 00:33:03 2012 +0200 @@ -50,6 +50,7 @@ org.jfree.data.time org.jfree.data.xy org.jfree.ui +org.jfree.data.gantt sun.swing sun.swing.table diff -r 9eed9fa986ad -r 5e8550c83f37 launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java Thu Sep 20 00:28:12 2012 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java Thu Sep 20 00:33:03 2012 +0200 @@ -196,8 +196,7 @@ // If this happens we definitely need to do something about it, and the // trace will be immeasurably helpful in figuring out what is wrong. out.println("Could not load necessary bundles for: " + cmdName); - e.printStackTrace(); - out.print(e.getStackTrace()); + e.printStackTrace(out); return; } diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineBean.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,86 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.common; + +import java.util.Date; + +public class ThreadTimelineBean { + + private long startTime; + private long stopTime; + private String name; + private Thread.State state; + + public Thread.State getState() { + return state; + } + + public void setState(Thread.State state) { + this.state = state; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getStopTime() { + return stopTime; + } + + public void setStopTime(long stopTime) { + this.stopTime = stopTime; + } + + @Override + public String toString() { + return "ThreadTimelineBean [name=" + name + ", state=" + state + + ", startTime=" + startTime + " (" + new Date(startTime) + ")" + + ", stopTime=" + stopTime + " (" + new Date(stopTime) + ")]"; + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineView.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.common; + +import java.util.List; +import java.util.Map; + +import com.redhat.thermostat.client.osgi.service.BasicView; +import com.redhat.thermostat.thread.model.ThreadInfoData; + +public abstract class ThreadTimelineView extends BasicView { + + public abstract void displayStats(Map> timelines, long start, long stop); + +} diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadView.java --- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadView.java Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadView.java Thu Sep 20 00:33:03 2012 +0200 @@ -74,6 +74,7 @@ public abstract VMThreadCapabilitiesView createVMThreadCapabilitiesView(); public abstract ThreadTableView createThreadTableView(); + public abstract ThreadTimelineView createThreadTimelineView(); public abstract void displayWarning(String warning); diff -r 9eed9fa986ad -r 5e8550c83f37 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 Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/locale/LocaleResources.java Thu Sep 20 00:33:03 2012 +0200 @@ -57,6 +57,7 @@ VM_CAPABILITIES, TABLE, DETAILS, + TIMELINE, LIVE_THREADS, DAEMON_THREADS, diff -r 9eed9fa986ad -r 5e8550c83f37 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 Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-common/src/main/resources/com/redhat/thermostat/thread/client/common/locale/strings.properties Thu Sep 20 00:33:03 2012 +0200 @@ -16,6 +16,7 @@ VM_CAPABILITIES = VM Capabilities TABLE = Table DETAILS = Details +TIMELINE = Timeline LIVE_THREADS = Live Threads DAEMON_THREADS = Daemon Threads diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/CommonController.java --- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/CommonController.java Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/CommonController.java Thu Sep 20 00:33:03 2012 +0200 @@ -36,6 +36,47 @@ package com.redhat.thermostat.thread.client.controller.impl; -public interface CommonController { - void initialize(); +import java.util.concurrent.TimeUnit; + +import com.redhat.thermostat.client.osgi.service.BasicView; +import com.redhat.thermostat.client.osgi.service.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; + +public abstract class CommonController { + + protected Timer timer; + protected BasicView view; + + public CommonController(Timer timer, BasicView view) { + this.view = view; + this.timer = timer; + } + + void initialize() { + timer.setInitialDelay(0); + timer.setDelay(1000); + timer.setTimeUnit(TimeUnit.MILLISECONDS); + timer.setSchedulingType(SchedulingType.FIXED_RATE); + + view.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + switch (actionEvent.getActionId()) { + case VISIBLE: + timer.start(); + break; + + case HIDDEN: + timer.stop(); + break; + + default: + break; + } + } + }); + } } diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInfoHelper.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/ThreadInfoHelper.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,74 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.redhat.thermostat.thread.model.ThreadInfoData; + +public class ThreadInfoHelper { + + /** + * Creates a {@link Map} whose keys are {@link ThreadInfoData} in the input + * list and whose values are all the {@link ThreadInfoData} equals to the + * key. + * + *

+ * + * Preserves the order of the input list. + * + *

+ * + * NOTE: The current invariant is that + * {@link ThreadInfoData} are equals if they have same thread id and name. + */ + public static Map> getThreadInfoDataMap(List infos) { + Map> stats = new HashMap<>(); + for (ThreadInfoData info : infos) { + List beanList = stats.get(info); + if (beanList == null) { + beanList = new ArrayList(); + stats.put(info, beanList); + } + beanList.add(info); + } + return stats; + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 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 Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java Thu Sep 20 00:33:03 2012 +0200 @@ -197,7 +197,8 @@ } private void initControllers() { - CommonController capsController = + + VMThreadCapabilitiesController capsController = new VMThreadCapabilitiesController(view.createVMThreadCapabilitiesView(), collector); capsController.initialize(); @@ -207,5 +208,10 @@ new ThreadTableController(threadTableView, collector, ApplicationContext.getInstance().getTimerFactory().createTimer()); threadTableController.initialize(); + + CommonController threadTimeline = + new ThreadTimelineController(view.createThreadTimelineView(), collector, + ApplicationContext.getInstance().getTimerFactory().createTimer()); + threadTimeline.initialize(); } } diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java --- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java Thu Sep 20 00:33:03 2012 +0200 @@ -41,60 +41,29 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; -import com.redhat.thermostat.client.osgi.service.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.ThreadTableBean; import com.redhat.thermostat.thread.client.common.ThreadTableView; import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; import com.redhat.thermostat.thread.model.ThreadInfoData; -public class ThreadTableController implements CommonController { +public class ThreadTableController extends CommonController { private ThreadTableView threadTableView; private ThreadCollector collector; - private Timer timer; public ThreadTableController(ThreadTableView threadTableView, ThreadCollector collector, Timer timer) { + super(timer, threadTableView); + timer.setAction(new ThreadTableControllerAction()); + this.collector = collector; this.threadTableView = threadTableView; - this.timer = timer; } - @Override - public void initialize() { - - timer.setInitialDelay(0); - timer.setDelay(1000); - timer.setTimeUnit(TimeUnit.MILLISECONDS); - timer.setSchedulingType(SchedulingType.FIXED_RATE); - timer.setAction(new ThreadTableControllerAction()); - - threadTableView.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - switch (actionEvent.getActionId()) { - case VISIBLE: - timer.start(); - break; - - case HIDDEN: - timer.stop(); - break; - - default: - break; - } - } - }); - } private class ThreadTableControllerAction implements Runnable { @Override @@ -110,15 +79,8 @@ // first, get a map of all threads with the respective info // the list will contain an ordered-by-timestamp list // with the known history for each thread - Map> stats = new HashMap<>(); - for (ThreadInfoData info : infos) { - List beanList = stats.get(info); - if (beanList == null) { - beanList = new ArrayList(); - stats.put(info, beanList); - } - beanList.add(info); - } + Map> stats = + ThreadInfoHelper.getThreadInfoDataMap(infos); List tableBeans = new ArrayList<>(); diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.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/ThreadTimelineController.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,124 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; +import com.redhat.thermostat.thread.client.common.ThreadTimelineView; +import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; +import com.redhat.thermostat.thread.model.ThreadInfoData; + +public class ThreadTimelineController extends CommonController { + + private ThreadTimelineView view; + private ThreadCollector collector; + + public ThreadTimelineController(ThreadTimelineView view, ThreadCollector collector, Timer timer) { + super(timer, view); + timer.setAction(new ThreadTimelineControllerAction()); + this.view = view; + this.collector = collector; + } + + private class ThreadTimelineControllerAction implements Runnable { + @Override + public void run() { + List infos = collector.getThreadInfo(); + if(infos.size() > 0) { + + Map> timelines = new HashMap<>(); + + Map> stats = + ThreadInfoHelper.getThreadInfoDataMap(infos); + for (List beanList : stats.values()) { + + // the list is ordered in most recent first + // the first element is the latest sample we have of this + // thread, so we use it as stop time. + + ThreadInfoData lastThreadInfo = beanList.get(beanList.size() - 1); + Thread.State lastState = lastThreadInfo.getState(); + + ThreadTimelineBean timeline = new ThreadTimelineBean(); + timeline.setName(lastThreadInfo.getThreadName()); + timeline.setState(lastThreadInfo.getState()); + timeline.setStartTime(lastThreadInfo.getTimeStamp()); + + long stopTime = beanList.get(0).getTimeStamp(); + timeline.setStopTime(stopTime); + + Stack threadTimelines = new Stack(); + timelines.put(lastThreadInfo, threadTimelines); + threadTimelines.push(timeline); + + for (int i = beanList.size() - 1; i >= 0; i--) { + ThreadInfoData threadInfo = beanList.get(i); + + Thread.State currentState = threadInfo.getState(); + if (currentState != lastState) { + lastState = currentState; + + timeline = threadTimelines.pop(); + timeline.setStopTime(threadInfo.getTimeStamp()); + + threadTimelines.push(timeline); + + timeline = new ThreadTimelineBean(); + timeline.setName(threadInfo.getThreadName()); + timeline.setState(threadInfo.getState()); + timeline.setStartTime(threadInfo.getTimeStamp()); + timeline.setStopTime(stopTime); + + lastThreadInfo = threadInfo; + lastState = currentState; + + // add the new thread stat + threadTimelines.push(timeline); + } + } + } + + view.displayStats(timelines, infos.get(infos.size() - 1).getTimeStamp(), infos.get(0).getTimeStamp()); + } + } + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesController.java --- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesController.java Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesController.java Thu Sep 20 00:33:03 2012 +0200 @@ -43,7 +43,7 @@ import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; import com.redhat.thermostat.thread.model.VMThreadCapabilities; -public class VMThreadCapabilitiesController implements CommonController { +public class VMThreadCapabilitiesController { private ThreadCollector collector; private VMThreadCapabilitiesView view; @@ -53,7 +53,6 @@ this.collector = collector; } - @Override public void initialize() { view.addActionListener(new ActionListener() { @Override diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInfoHelperTest.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/ThreadInfoHelperTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,94 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import com.redhat.thermostat.thread.model.ThreadInfoData; + +public class ThreadInfoHelperTest { + + @Test + public void verifyMap() { + ThreadInfoData data1 = new ThreadInfoData(); + data1.setThreadName("test1"); + data1.setThreadId(1); + data1.setState(Thread.State.RUNNABLE); + + ThreadInfoData data2 = new ThreadInfoData(); + data2.setThreadName("test2"); + data2.setThreadId(2); + data2.setState(Thread.State.BLOCKED); + + ThreadInfoData data3 = new ThreadInfoData(); + data3.setThreadName("test1"); + data3.setThreadId(1); + data3.setState(Thread.State.TIMED_WAITING); + + ThreadInfoData data4 = new ThreadInfoData(); + data4.setThreadName("test2"); + data4.setThreadId(2); + data4.setState(Thread.State.RUNNABLE); + + List infos = new ArrayList<>(); + infos.add(data1); + infos.add(data2); + infos.add(data3); + infos.add(data4); + + Map> result = ThreadInfoHelper.getThreadInfoDataMap(infos); + assertEquals(2, result.size()); + + assertTrue(result.containsKey(data1)); + assertTrue(result.containsKey(data2)); + + assertEquals(2, result.get(data1).size()); + assertEquals(2, result.get(data2).size()); + + assertEquals(Thread.State.RUNNABLE, result.get(data1).get(0).getState()); + assertEquals(Thread.State.TIMED_WAITING, result.get(data1).get(1).getState()); + + assertEquals(Thread.State.BLOCKED, result.get(data2).get(0).getState()); + assertEquals(Thread.State.RUNNABLE, result.get(data2).get(1).getState()); + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 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 Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -60,6 +60,7 @@ import com.redhat.thermostat.thread.client.common.ThreadTableBean; import com.redhat.thermostat.thread.client.common.ThreadTableView; import com.redhat.thermostat.thread.client.common.ThreadTableView.ThreadSelectionAction; +import com.redhat.thermostat.thread.client.common.ThreadTimelineView; import com.redhat.thermostat.thread.client.common.ThreadView; import com.redhat.thermostat.thread.client.common.ThreadViewProvider; import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView; @@ -80,6 +81,7 @@ private ThreadTableView threadTableView; private VMThreadCapabilitiesView threadCapsView; + private ThreadTimelineView threadTimelineView; @Before public void setUp() { @@ -96,13 +98,15 @@ private void setUpView() { threadCapsView = mock(VMThreadCapabilitiesView.class); threadTableView = mock(ThreadTableView.class); - + threadTimelineView = mock(ThreadTimelineView.class); + view = mock(ThreadView.class); viewFactory = mock(ThreadViewProvider.class); when(viewFactory.createView()).thenReturn(view); when(view.createVMThreadCapabilitiesView()).thenReturn(threadCapsView); when(view.createThreadTableView()).thenReturn(threadTableView); + when(view.createThreadTimelineView()).thenReturn(threadTimelineView); } private void setUpTimers() { @@ -152,6 +156,7 @@ verify(view).createThreadTableView(); verify(view).createVMThreadCapabilitiesView(); + verify(view).createThreadTimelineView(); } @Test diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java --- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -68,7 +68,7 @@ collector = mock(ThreadCollector.class); timer = mock(Timer.class); - + view = mock(ThreadTableView.class); setUpTimers(); diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineControllerTest.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/ThreadTimelineControllerTest.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,192 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.junit.Assert.*; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.client.osgi.service.BasicView; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.common.appctx.ApplicationContextUtil; +import com.redhat.thermostat.thread.client.common.ThreadTableView; +import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; +import com.redhat.thermostat.thread.client.common.ThreadTimelineView; +import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; +import com.redhat.thermostat.thread.model.ThreadInfoData; + +public class ThreadTimelineControllerTest { + + private ThreadTimelineView view; + private ThreadCollector collector; + + private Timer timer; + + private ActionListener actionListener; + ArgumentCaptor timerActionCaptor; + + @Before + public void setUp() { + ApplicationContextUtil.resetApplicationContext(); + collector = mock(ThreadCollector.class); + + timer = mock(Timer.class); + + view = mock(ThreadTimelineView.class); + + setUpTimers(); + } + + private void setUpTimers() { + timer = mock(Timer.class); + timerActionCaptor = ArgumentCaptor.forClass(Runnable.class); + doNothing().when(timer).setAction(timerActionCaptor.capture()); + } + + @Test + public void testDisplayStats() { + + ThreadInfoData data1 = new ThreadInfoData(); + data1.setThreadName("test1"); + data1.setThreadId(1); + data1.setState(Thread.State.RUNNABLE); + data1.setTimeStamp(100); + + ThreadInfoData data2 = new ThreadInfoData(); + data2.setThreadName("test2"); + data2.setThreadId(2); + data2.setTimeStamp(1000); + data2.setState(Thread.State.BLOCKED); + + ThreadInfoData data3 = new ThreadInfoData(); + data3.setThreadName("test1"); + data3.setThreadId(1); + data3.setState(Thread.State.TIMED_WAITING); + data3.setTimeStamp(200); + + ThreadInfoData data4 = new ThreadInfoData(); + data4.setThreadName("test2"); + data4.setThreadId(2); + data4.setState(Thread.State.BLOCKED); + data4.setTimeStamp(2000); + + ThreadInfoData data5 = new ThreadInfoData(); + data5.setThreadName("test2"); + data5.setThreadId(2); + data5.setState(Thread.State.RUNNABLE); + data5.setTimeStamp(3000); + + List infos = new ArrayList<>(); + // descending order + infos.add(data5); + infos.add(data4); + infos.add(data2); + infos.add(data3); + infos.add(data1); + + when(collector.getThreadInfo()).thenReturn(infos); + + ArgumentCaptor timerCaptor = ArgumentCaptor.forClass(Runnable.class); + doNothing().when(timer).setAction(timerCaptor.capture()); + + ThreadTimelineController controller = new ThreadTimelineController(view, collector, timer); + controller.initialize(); + + Runnable action = timerCaptor.getValue(); + action.run(); + + ArgumentCaptor mapCaptor = ArgumentCaptor.forClass(Map.class); + + verify(view).displayStats(mapCaptor.capture(), anyLong(), anyLong()); + + Map viewResult = mapCaptor.getValue(); + assertEquals(2, viewResult.size()); + + List beanList = (List) viewResult.get(data1); + + assertEquals(2, beanList.size()); + + assertEquals("test1", beanList.get(0).getName()); + assertEquals("test1", beanList.get(1).getName()); + + beanList = (List) viewResult.get(data2); + assertEquals(2, beanList.size()); + + assertEquals("test2", beanList.get(0).getName()); + assertEquals("test2", beanList.get(1).getName()); + + assertEquals(1000, beanList.get(0).getStartTime()); + assertEquals(3000, beanList.get(0).getStopTime()); + assertEquals(Thread.State.BLOCKED, beanList.get(0).getState()); + + assertEquals(3000, beanList.get(1).getStartTime()); + assertEquals(Thread.State.BLOCKED, beanList.get(0).getState()); + } + + @Test + public void testStartThreadTimelineController() { + + ArgumentCaptor viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class); + doNothing().when(view).addActionListener(viewArgumentCaptor.capture()); + + ThreadTimelineController controller = new ThreadTimelineController(view, collector, timer); + controller.initialize(); + + actionListener = viewArgumentCaptor.getValue(); + actionListener.actionPerformed(new ActionEvent<>(view, BasicView.Action.VISIBLE)); + + verify(timer).start(); + + actionListener.actionPerformed(new ActionEvent<>(view, BasicView.Action.HIDDEN)); + + verify(timer).stop(); + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadDetailsView.java --- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadDetailsView.java Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadDetailsView.java Thu Sep 20 00:33:03 2012 +0200 @@ -36,8 +36,11 @@ package com.redhat.thermostat.thread.client.swing.impl; +import java.awt.BorderLayout; import java.awt.Component; +import javax.swing.ImageIcon; +import javax.swing.JLabel; import javax.swing.JPanel; import com.redhat.thermostat.client.ui.SwingComponent; @@ -48,15 +51,6 @@ import com.redhat.thermostat.thread.client.common.chart.ThreadDeatailsPieChart; import com.redhat.thermostat.thread.client.common.locale.LocaleResources; -import javax.swing.GroupLayout; -import javax.swing.GroupLayout.Alignment; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import java.awt.GridBagLayout; -import java.awt.GridBagConstraints; -import java.awt.BorderLayout; -import java.awt.GridLayout; - public class SwingThreadDetailsView extends ThreadDetailsView implements SwingComponent { private JPanel details; diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineChart.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/SwingThreadTimelineChart.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,197 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.BasicStroke; +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.geom.Area; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.swing.GradientRoundBorder; +import com.redhat.thermostat.swing.GraphicsUtils; +import com.redhat.thermostat.swing.Palette; +import com.redhat.thermostat.swing.models.LongRange; +import com.redhat.thermostat.swing.models.LongRangeNormalizer; +import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; + +@SuppressWarnings("serial") +public class SwingThreadTimelineChart extends JPanel { + + private List timeline; + + private LongRangeNormalizer normalizer; + + public SwingThreadTimelineChart(List timeline, long rangeStart, long rangeStop) { + this.timeline = timeline; + + LongRange range = new LongRange(); + range.setMin(rangeStart); + range.setMax(rangeStop); + + setBorder(new GradientRoundBorder()); + normalizer = new LongRangeNormalizer(range); + } + + @Override + protected void paintComponent(Graphics g) { + + normalizer.setMinNormalized(0); + normalizer.setMaxNormalized(getWidth() - 3); + + GraphicsUtils utils = GraphicsUtils.getInstance(); + + Graphics2D graphics = utils.createAAGraphics(g); + + int y = getHeight()/3; + graphics.clearRect(0, 0, getWidth(), getHeight()); + graphics.drawString(timeline.get(0).getName(), 2, y); + + y = getHeight()/2; + + int w = getWidth() - 4; + int h = 10; + + Shape shape = utils.getRoundShape(w, h); + + graphics.translate(2, y); + + Paint paint = new GradientPaint(0, 0, Palette.DARK_GRAY.getColor(), 0, h, Palette.WHITE.getColor()); + graphics.setPaint(paint); + graphics.fill(shape); + + for (ThreadTimelineBean thread : timeline) { + normalizer.setValue(thread.getStartTime()); + int x0 = (int) normalizer.getValueNormalized(); + + normalizer.setValue(thread.getStopTime()); + int x1 = (int) normalizer.getValueNormalized(); + + graphics.setColor(getColor(thread)); + graphics.fillRect(x0, 1, x1 - x0, h - 1); + } + + graphics.setColor(getColor(timeline.get(timeline.size() - 1))); + graphics.draw(shape); + graphics.dispose(); + } + + private Color getColor(ThreadTimelineBean thread) { + Color result = null; + + switch (thread.getState()) { + case TIMED_WAITING: + result = Palette.PALE_RED.getColor(); + break; + + case NEW: + result = Palette.POMP_AND_POWER_VIOLET.getColor(); + break; + + case RUNNABLE: + result = Palette.PRUSSIAN_BLUE.getColor(); + break; + + case TERMINATED: + result = Palette.GRAY.getColor(); + break; + + case BLOCKED: + result = Palette.RED.getColor(); + break; + + case WAITING: + result = Palette.GRANITA_ORANGE.getColor(); + break; + + default: + result = Color.BLACK; + break; + } + return result; + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + ThreadTimelineBean bean1 = new ThreadTimelineBean(); + bean1.setName("test"); + bean1.setStartTime(0); + bean1.setStopTime(1000); + bean1.setState(Thread.State.BLOCKED); + + ThreadTimelineBean bean2 = new ThreadTimelineBean(); + bean2.setName("test"); + bean2.setStartTime(1000); + bean2.setStopTime(2000); + bean2.setState(Thread.State.RUNNABLE); + + ThreadTimelineBean bean3 = new ThreadTimelineBean(); + bean3.setName("test"); + bean3.setStartTime(2000); + bean3.setStopTime(2100); + bean3.setState(Thread.State.TIMED_WAITING); + + List timeline = new ArrayList<>(); + timeline.add(bean1); + timeline.add(bean2); + timeline.add(bean3); + + SwingThreadTimelineChart chart = new SwingThreadTimelineChart(timeline, 0, 2500); + + frame.add(chart); + frame.setSize(800, 150); + + frame.setVisible(true); + } + }); + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.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/SwingThreadTimelineView.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,118 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.util.List; +import java.util.Map; + +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.client.ui.ComponentVisibleListener; +import com.redhat.thermostat.client.ui.SwingComponent; +import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; +import com.redhat.thermostat.thread.client.common.ThreadTimelineView; +import com.redhat.thermostat.thread.model.ThreadInfoData; + +public class SwingThreadTimelineView extends ThreadTimelineView implements SwingComponent { + + private JPanel timeLinePanel; + private DefaultListModel model; + + public SwingThreadTimelineView() { + timeLinePanel = new JPanel(); + timeLinePanel.addHierarchyListener(new ComponentVisibleListener() { + @Override + public void componentShown(Component component) { + SwingThreadTimelineView.this.notify(Action.VISIBLE); + } + + @Override + public void componentHidden(Component component) { + SwingThreadTimelineView.this.notify(Action.HIDDEN); + } + }); + + timeLinePanel.setLayout(new BorderLayout(0, 0)); + model = new DefaultListModel<>(); + JList chartList = new JList<>(model); + + chartList.setLayoutOrientation(JList.VERTICAL); + chartList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + JScrollPane scrollPane = new JScrollPane(chartList); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + timeLinePanel.add(scrollPane); + chartList.setCellRenderer(new ListCellRenderer() { + @Override + public Component getListCellRendererComponent( + JList list, SwingThreadTimelineChart value, + int index, boolean isSelected, boolean cellHasFocus) { + return value; + } + }); + } + + @Override + public void displayStats(final Map> timelines, final long start, final long stop) { + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + model.clear(); + for (List timeline : timelines.values()) { + SwingThreadTimelineChart panel = new SwingThreadTimelineChart(timeline, start, stop); + panel.setPreferredSize(new Dimension(timeLinePanel.getWidth(), 50)); + model.addElement(panel); + } + } + }); + } + + @Override + public Component getUiComponent() { + return timeLinePanel; + } +} diff -r 9eed9fa986ad -r 5e8550c83f37 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 Thu Sep 20 00:28:12 2012 +0200 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java Thu Sep 20 00:33:03 2012 +0200 @@ -56,6 +56,7 @@ import com.redhat.thermostat.swing.ChartPanel; import com.redhat.thermostat.thread.client.common.ThreadTableBean; import com.redhat.thermostat.thread.client.common.ThreadTableView; +import com.redhat.thermostat.thread.client.common.ThreadTimelineView; import com.redhat.thermostat.thread.client.common.ThreadView; import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView; import com.redhat.thermostat.thread.client.common.chart.LivingDaemonThreadDifferenceChart; @@ -70,8 +71,9 @@ private SwingThreadTableView threadTableView; private SwingVMThreadCapabilitiesView vmCapsView; + private SwingThreadTimelineView threadTimelineView; private SwingThreadDetailsView threadDetailsView; - + private JTabbedPane pane; private static final Translate t = LocaleResources.createLocalizer(); @@ -152,6 +154,9 @@ pane.addTab(t.localize(LocaleResources.DETAILS), threadDetailsView.getUiComponent()); threadDetailsPaneID = 2; + threadTimelineView = new SwingThreadTimelineView(); + pane.addTab(t.localize(LocaleResources.TIMELINE), threadTimelineView.getUiComponent()); + panel.getSplitPane().setBottomComponent(pane); } @@ -261,4 +266,9 @@ } }); } + + @Override + public ThreadTimelineView createThreadTimelineView() { + return threadTimelineView; + } } diff -r 9eed9fa986ad -r 5e8550c83f37 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTimelineChart.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/ThreadTimelineChart.java Thu Sep 20 00:33:03 2012 +0200 @@ -0,0 +1,89 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.Graphics; +import java.awt.Graphics2D; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ColorUIResource; + +import com.redhat.thermostat.swing.GraphicsUtils; + +@SuppressWarnings("serial") +public class ThreadTimelineChart extends JPanel { + + private static final ColorUIResource TICK_COLOR = new ColorUIResource(0xa8aca8); + + public ThreadTimelineChart() { + } + + @Override + protected void paintComponent(Graphics g) { + Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); + + graphics.setColor(TICK_COLOR); + + int x = 1; + int y = 2; + int w = getWidth() - 2; + int h = getHeight(); + + graphics.drawLine(x, y, w, y); + + graphics.dispose(); + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + ThreadTimelineChart chart = new ThreadTimelineChart(); + + frame.add(chart); + frame.setSize(500, 500); + frame.setVisible(true); + } + }); + } +}