# HG changeset patch # User Omair Majid # Date 1367526084 14400 # Node ID 00c6bf165dcecf81631827513271208e9945b1bc # Parent 0e9e22f2696a53bf1e602d07bc28f4ff0f5d2dd6 Connection pool for JMX connections Reviewed-by: neugens, ebaron Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-April/006456.html diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/pom.xml --- a/agent/core/pom.xml Mon Apr 29 11:54:04 2013 -0400 +++ b/agent/core/pom.xml Thu May 02 16:21:24 2013 -0400 @@ -111,6 +111,7 @@ Red Hat, Inc. com.redhat.thermostat.agent.core + com.redhat.thermostat.agent.internal.Activator com.redhat.thermostat.agent, com.redhat.thermostat.agent.config, @@ -120,7 +121,9 @@ com.redhat.thermostat.utils.management, + com.redhat.thermostat.agent.internal, com.redhat.thermostat.backend.internal, + com.redhat.thermostat.utils.management.internal, <_nouses>true diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java Thu May 02 16:21:24 2013 -0400 @@ -0,0 +1,60 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.agent.internal; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; +import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl; + +public class Activator implements BundleActivator { + + private ServiceRegistration registration; + + @Override + public void start(BundleContext context) throws Exception { + registration = context.registerService(MXBeanConnectionPool.class, new MXBeanConnectionPoolImpl(), null); + } + + @Override + public void stop(BundleContext context) throws Exception { + registration.unregister(); + } + +} diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java --- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java Mon Apr 29 11:54:04 2013 -0400 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java Thu May 02 16:21:24 2013 -0400 @@ -36,33 +36,10 @@ package com.redhat.thermostat.utils.management; -import java.io.Closeable; -import java.io.IOException; +import javax.management.MalformedObjectNameException; -import javax.management.JMX; -import javax.management.MBeanServerConnection; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import javax.management.remote.JMXConnector; - -public class MXBeanConnection implements Closeable { +public interface MXBeanConnection { - private JMXConnector connection; - private MBeanServerConnection mbsc; - - MXBeanConnection(JMXConnector connection, MBeanServerConnection mbsc) { - this.connection = connection; - this.mbsc = mbsc; - } - - public synchronized E createProxy(String name, Class proxyClass) throws MalformedObjectNameException { - ObjectName objectName = new ObjectName(name); - return JMX.newMXBeanProxy(mbsc, objectName, proxyClass); - } - - @Override - public void close() throws IOException { - connection.close(); - } + E createProxy(String name, Class proxyClass) throws MalformedObjectNameException; + } - diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnectionPool.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnectionPool.java Thu May 02 16:21:24 2013 -0400 @@ -0,0 +1,50 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.utils.management; + +import com.redhat.thermostat.annotations.Service; + +/** + * A pool for caching and reusing MXBeanConnections. + */ +@Service +public interface MXBeanConnectionPool { + + MXBeanConnection acquire(int pid) throws Exception; + + void release(int pid, MXBeanConnection connection) throws Exception; +} diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnector.java --- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnector.java Mon Apr 29 11:54:04 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.utils.management; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.util.Properties; - -import javax.management.MBeanServerConnection; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import javax.management.remote.JMXServiceURL; - -import com.redhat.thermostat.storage.core.VmRef; -import com.sun.tools.attach.VirtualMachine; - -public class MXBeanConnector implements Closeable { - - private static final String CONNECTOR_ADDRESS_PROPERTY = "com.sun.management.jmxremote.localConnectorAddress"; - private String connectorAddress; - - private VirtualMachine vm; - - private boolean attached; - - private String reference; - - public MXBeanConnector(String reference) { - this.reference = reference; - } - - public MXBeanConnector(VmRef reference) { - this.reference = reference.getStringID(); - } - - public synchronized void attach() throws Exception { - if (attached) - throw new IOException("Already attached"); - - vm = VirtualMachine.attach(reference); - attached = true; - - Properties props = vm.getAgentProperties(); - connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); - if (connectorAddress == null) { - props = vm.getSystemProperties(); - String home = props.getProperty("java.home"); - String agent = home + File.separator + "lib" + File.separator + "management-agent.jar"; - vm.loadAgent(agent); - - props = vm.getAgentProperties(); - connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); - } - } - - public synchronized MXBeanConnection connect() throws Exception { - - if (!attached) - throw new IOException("Agent not attached to target VM"); - - JMXServiceURL url = new JMXServiceURL(connectorAddress); - JMXConnector connection = JMXConnectorFactory.connect(url); - MBeanServerConnection mbsc = null; - try { - mbsc = connection.getMBeanServerConnection(); - - } catch (IOException e) { - connection.close(); - throw e; - } - - return new MXBeanConnection(connection, mbsc); - } - - public boolean isAttached() { - return attached; - } - - @Override - public synchronized void close() throws IOException { - if (attached) { - vm.detach(); - attached = false; - } - } -} - diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionImpl.java Thu May 02 16:21:24 2013 -0400 @@ -0,0 +1,68 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.utils.management.internal; + +import java.io.IOException; + +import javax.management.JMX; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; + +import com.redhat.thermostat.utils.management.MXBeanConnection; + +class MXBeanConnectionImpl implements MXBeanConnection { + + private JMXConnector connection; + private MBeanServerConnection mbsc; + + MXBeanConnectionImpl(JMXConnector connection, MBeanServerConnection mbsc) { + this.connection = connection; + this.mbsc = mbsc; + } + + public synchronized E createProxy(String name, Class proxyClass) throws MalformedObjectNameException { + ObjectName objectName = new ObjectName(name); + return JMX.newMXBeanProxy(mbsc, objectName, proxyClass); + } + + void close() throws IOException { + connection.close(); + } +} + diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImpl.java Thu May 02 16:21:24 2013 -0400 @@ -0,0 +1,97 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.utils.management.internal; + +import java.util.HashMap; +import java.util.Map; + +import com.redhat.thermostat.common.Pair; +import com.redhat.thermostat.utils.management.MXBeanConnection; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; + +public class MXBeanConnectionPoolImpl implements MXBeanConnectionPool { + + // pid -> (usageCount, actualObject) + private Map> pool = new HashMap<>(); + + private ConnectorCreator creator; + + public MXBeanConnectionPoolImpl() { + this(new ConnectorCreator()); + } + + MXBeanConnectionPoolImpl(ConnectorCreator connectorCreator) { + this.creator = connectorCreator; + } + + @Override + public synchronized MXBeanConnection acquire(int pid) throws Exception { + Pair data = pool.get(pid); + if (data == null) { + MXBeanConnector connector = creator.create(pid); + connector.attach(); + MXBeanConnectionImpl connection = connector.connect(); + connector.close(); + data = new Pair(1, connection); + } else { + data = new Pair<>(data.getFirst() + 1, data.getSecond()); + } + pool.put(pid, data); + return data.getSecond(); + } + + @Override + public synchronized void release(int pid, MXBeanConnection toRelese) throws Exception { + Pair data = pool.get(pid); + MXBeanConnectionImpl connection = data.getSecond(); + int usageCount = data.getFirst(); + usageCount--; + if (usageCount == 0) { + connection.close(); + pool.remove(pid); + } else { + data = new Pair<>(usageCount, connection); + pool.put(pid, data); + } + } + + static class ConnectorCreator { + public MXBeanConnector create(int pid) { + return new MXBeanConnector(String.valueOf(pid)); + } + } +} diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnector.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnector.java Thu May 02 16:21:24 2013 -0400 @@ -0,0 +1,122 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.utils.management.internal; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +import com.redhat.thermostat.storage.core.VmRef; +import com.sun.tools.attach.VirtualMachine; + +class MXBeanConnector implements Closeable { + + private static final String CONNECTOR_ADDRESS_PROPERTY = "com.sun.management.jmxremote.localConnectorAddress"; + private String connectorAddress; + + private VirtualMachine vm; + + private boolean attached; + + private String reference; + + public MXBeanConnector(String reference) { + this.reference = reference; + } + + public MXBeanConnector(VmRef reference) { + this.reference = reference.getStringID(); + } + + public synchronized void attach() throws Exception { + if (attached) + throw new IOException("Already attached"); + + vm = VirtualMachine.attach(reference); + attached = true; + + Properties props = vm.getAgentProperties(); + connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); + if (connectorAddress == null) { + props = vm.getSystemProperties(); + String home = props.getProperty("java.home"); + String agent = home + File.separator + "lib" + File.separator + "management-agent.jar"; + vm.loadAgent(agent); + + props = vm.getAgentProperties(); + connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); + } + } + + public synchronized MXBeanConnectionImpl connect() throws IOException { + + if (!attached) + throw new IOException("Agent not attached to target VM"); + + JMXServiceURL url = new JMXServiceURL(connectorAddress); + JMXConnector connection = JMXConnectorFactory.connect(url); + MBeanServerConnection mbsc = null; + try { + mbsc = connection.getMBeanServerConnection(); + + } catch (IOException e) { + connection.close(); + throw e; + } + + return new MXBeanConnectionImpl(connection, mbsc); + } + + public boolean isAttached() { + return attached; + } + + @Override + public synchronized void close() throws IOException { + if (attached) { + vm.detach(); + attached = false; + } + } +} + diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java Thu May 02 16:21:24 2013 -0400 @@ -0,0 +1,64 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.agent.internal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.redhat.thermostat.testutils.StubBundleContext; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; +import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl; + +public class ActivatorTest { + @Test + public void verifyServiceIsRegistered() throws Exception { + + StubBundleContext context = new StubBundleContext(); + + Activator activator = new Activator(); + + activator.start(context); + + assertTrue(context.isServiceRegistered(MXBeanConnectionPool.class.getName(), MXBeanConnectionPoolImpl.class)); + + activator.stop(context); + + assertFalse(context.isServiceRegistered(MXBeanConnectionPool.class.getName(), MXBeanConnectionPoolImpl.class)); + } +} diff -r 0e9e22f2696a -r 00c6bf165dce agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImplTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImplTest.java Thu May 02 16:21:24 2013 -0400 @@ -0,0 +1,147 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.utils.management.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.utils.management.MXBeanConnection; +import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl.ConnectorCreator; +import com.redhat.thermostat.utils.management.internal.MXBeanConnector; + +public class MXBeanConnectionPoolImplTest { + + @Test + public void testAcquire() throws Exception { + MXBeanConnectionImpl toReturn = mock(MXBeanConnectionImpl.class); + MXBeanConnector connector = mock(MXBeanConnector.class); + ConnectorCreator creator = mock(ConnectorCreator.class); + + when(creator.create(anyInt())).thenReturn(connector); + when(connector.connect()).thenReturn(toReturn); + + MXBeanConnectionPoolImpl pool = new MXBeanConnectionPoolImpl(creator); + + MXBeanConnection connection = pool.acquire(0); + + assertNotNull(connection); + assertEquals(connection, toReturn); + + verify(connector).attach(); + verify(connector).connect(); + verify(connector).close(); + } + + @Test + public void testAcquireTwice() throws Exception { + MXBeanConnectionImpl toReturn = mock(MXBeanConnectionImpl.class); + MXBeanConnector connector = mock(MXBeanConnector.class); + ConnectorCreator creator = mock(ConnectorCreator.class); + + when(creator.create(anyInt())).thenReturn(connector); + when(connector.connect()).thenReturn(toReturn); + + MXBeanConnectionPoolImpl pool = new MXBeanConnectionPoolImpl(creator); + + MXBeanConnection connection1 = pool.acquire(0); + + verify(connector).attach(); + verify(connector).connect(); + verify(connector).close(); + + MXBeanConnection connection2 = pool.acquire(0); + + assertEquals(connection1, toReturn); + assertEquals(connection2, toReturn); + + verifyNoMoreInteractions(connector); + } + + @Test + public void testRelease() throws Exception { + MXBeanConnectionImpl actualConnection = mock(MXBeanConnectionImpl.class); + MXBeanConnector connector = mock(MXBeanConnector.class); + ConnectorCreator creator = mock(ConnectorCreator.class); + + when(creator.create(anyInt())).thenReturn(connector); + when(connector.connect()).thenReturn(actualConnection); + + MXBeanConnectionPoolImpl pool = new MXBeanConnectionPoolImpl(creator); + + MXBeanConnection connection = pool.acquire(0); + + verify(actualConnection, never()).close(); + + pool.release(0, connection); + + verify(actualConnection).close(); + } + + @Test + public void testReleaseTwice() throws Exception { + MXBeanConnectionImpl actualConnection = mock(MXBeanConnectionImpl.class); + MXBeanConnector connector = mock(MXBeanConnector.class); + ConnectorCreator creator = mock(ConnectorCreator.class); + + when(creator.create(anyInt())).thenReturn(connector); + when(connector.connect()).thenReturn(actualConnection); + + MXBeanConnectionPoolImpl pool = new MXBeanConnectionPoolImpl(creator); + + // connection1 == connection1 == actualConnection + MXBeanConnection connection1 = pool.acquire(0); + MXBeanConnection connection2 = pool.acquire(0); + + pool.release(0, connection1); + + verify(actualConnection, never()).close(); + + pool.release(0, connection2); + + verify(actualConnection).close(); + + } +} diff -r 0e9e22f2696a -r 00c6bf165dce thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java --- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java Mon Apr 29 11:54:04 2013 -0400 +++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java Thu May 02 16:21:24 2013 -0400 @@ -57,7 +57,7 @@ import com.redhat.thermostat.thread.model.ThreadSummary; import com.redhat.thermostat.thread.model.VMThreadCapabilities; import com.redhat.thermostat.utils.management.MXBeanConnection; -import com.redhat.thermostat.utils.management.MXBeanConnector; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; @SuppressWarnings("restriction") class Harvester { @@ -68,18 +68,17 @@ private ScheduledExecutorService threadPool; private ScheduledFuture harvester; - MXBeanConnector connector; - + private MXBeanConnectionPool connectionPool; private MXBeanConnection connection; private ThreadMXBean collectorBean; private ThreadDao threadDao; - private String vmId; + private int vmId; - Harvester(ThreadDao threadDao, ScheduledExecutorService threadPool, String vmId) { - this.connector = new MXBeanConnector(vmId); + Harvester(ThreadDao threadDao, ScheduledExecutorService threadPool, String vmId, MXBeanConnectionPool connectionPool) { this.threadDao = threadDao; - this.vmId = vmId; + this.vmId = Integer.valueOf(vmId); this.threadPool = threadPool; + this.connectionPool = connectionPool; } synchronized boolean start() { @@ -87,17 +86,8 @@ return true; } - if (!connector.isAttached()) { - try { - connector.attach(); - } catch (Exception ex) { - logger.log(Level.SEVERE, "can't attach", ex); - return false; - } - } - try { - connection = connector.connect(); + connection = connectionPool.acquire(vmId); } catch (Exception ex) { logger.log(Level.SEVERE, "can't connect", ex); return false; @@ -126,24 +116,11 @@ isConnected = false; - boolean stillConnected = false; try { - connection.close(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "can't close connection", ex); - stillConnected = true; - } - - if (connector.isAttached()) { - try { - connector.close(); - } catch (Exception ex) { - logger.log(Level.SEVERE, "can't detach", ex); - if (stillConnected) { - isConnected = true; - } - return false; - } + connectionPool.release(vmId, connection); + } catch (Exception ex) { + logger.log(Level.SEVERE, "can't disconnect", ex); + return false; } return true; @@ -178,7 +155,7 @@ summary.setCurrentLiveThreads(collectorBean.getThreadCount()); summary.setCurrentDaemonThreads(collectorBean.getDaemonThreadCount()); summary.setTimeStamp(timestamp); - summary.setVmId(Integer.parseInt(vmId)); + summary.setVmId(vmId); threadDao.saveSummary(summary); long [] ids = collectorBean.getAllThreadIds(); @@ -221,7 +198,7 @@ info.setAllocatedBytes(allocatedBytes[i]); } - info.setVmId(Integer.parseInt(vmId)); + info.setVmId(vmId); threadDao.saveThreadInfo(info); } @@ -238,63 +215,47 @@ } synchronized boolean saveVmCaps() { - - boolean closeAfter = false; - if (!connector.isAttached()) { - closeAfter = true; + boolean success = false; + + try { + MXBeanConnection connection = connectionPool.acquire(vmId); try { - connector.attach(); + + ThreadMXBean bean = getDataCollectorBean(connection); + VMThreadCapabilities caps = new VMThreadCapabilities(); + + List features = new ArrayList<>(3); + if (bean.isThreadCpuTimeSupported()) + features.add(ThreadDao.CPU_TIME); + if (bean.isThreadContentionMonitoringSupported()) + features.add(ThreadDao.CONTENTION_MONITOR); + + if (bean instanceof com.sun.management.ThreadMXBean) { + com.sun.management.ThreadMXBean sunBean = (com.sun.management.ThreadMXBean) bean; + + try { + if (sunBean.isThreadAllocatedMemorySupported()) { + features.add(ThreadDao.THREAD_ALLOCATED_MEMORY); + } + } catch (Exception ignore) { + } + ; + } + caps.setSupportedFeaturesList(features.toArray(new String[features.size()])); + caps.setVmId(vmId); + threadDao.saveCapabilities(caps); + } catch (Exception ex) { - logger.log(Level.SEVERE, "can't attach", ex); - if (closeAfter) { - closeConnection(); - } - return false; + logger.log(Level.SEVERE, "can't get MXBeanConnection connection", ex); + success = false; } + connectionPool.release(vmId, connection); + } catch (Exception e) { + logger.log(Level.SEVERE, "can't get MXBeanConnection connection", e); + success = false; } - try (MXBeanConnection connection = connector.connect()) { - - ThreadMXBean bean = getDataCollectorBean(connection); - VMThreadCapabilities caps = new VMThreadCapabilities(); - - List features = new ArrayList<>(3); - if (bean.isThreadCpuTimeSupported()) - features.add(ThreadDao.CPU_TIME); - if (bean.isThreadContentionMonitoringSupported()) - features.add(ThreadDao.CONTENTION_MONITOR); - - if (bean instanceof com.sun.management.ThreadMXBean) { - com.sun.management.ThreadMXBean sunBean = (com.sun.management.ThreadMXBean) bean; - - try { - if (sunBean.isThreadAllocatedMemorySupported()) { - features.add(ThreadDao.THREAD_ALLOCATED_MEMORY); - } - } catch (Exception ignore) {}; - } - caps.setSupportedFeaturesList(features.toArray(new String[features.size()])); - caps.setVmId(Integer.parseInt(vmId)); - threadDao.saveCapabilities(caps); - - } catch (Exception ex) { - logger.log(Level.SEVERE, "can't get MXBeanConnection connection", ex); - return false; - } - - if (closeAfter) { - closeConnection(); - } - - return true; - } - - private void closeConnection() { - try { - connector.close(); - } catch (Exception ex) { - logger.log(Level.SEVERE, "can't close connection to vm", ex); - } + return success; } } diff -r 0e9e22f2696a -r 00c6bf165dce thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java --- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java Mon Apr 29 11:54:04 2013 -0400 +++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java Thu May 02 16:21:24 2013 -0400 @@ -54,6 +54,7 @@ import com.redhat.thermostat.thread.collector.HarvesterCommand; import com.redhat.thermostat.thread.dao.ThreadDao; import com.redhat.thermostat.thread.model.ThreadHarvestingStatus; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; public class ThreadHarvester implements RequestReceiver { @@ -62,15 +63,17 @@ private ThreadDao dao; private Clock clock; + private MXBeanConnectionPool connectionPool; - public ThreadHarvester(ScheduledExecutorService executor) { - this(executor, new SystemClock()); + public ThreadHarvester(ScheduledExecutorService executor, MXBeanConnectionPool pool) { + this(executor, new SystemClock(), pool); } - public ThreadHarvester(ScheduledExecutorService executor, Clock clock) { + public ThreadHarvester(ScheduledExecutorService executor, Clock clock, MXBeanConnectionPool connectionPool) { this.executor = executor; this.connectors = new HashMap<>(); this.clock = clock; + this.connectionPool = connectionPool; } /** @@ -186,7 +189,7 @@ } Harvester createHarvester(String vmId) { - return new Harvester(dao, executor, vmId); + return new Harvester(dao, executor, vmId, connectionPool); } /** diff -r 0e9e22f2696a -r 00c6bf165dce thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/osgi/Activator.java --- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/osgi/Activator.java Mon Apr 29 11:54:04 2013 -0400 +++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/osgi/Activator.java Thu May 02 16:21:24 2013 -0400 @@ -52,11 +52,13 @@ import com.redhat.thermostat.thread.dao.ThreadDao; import com.redhat.thermostat.thread.harvester.ThreadBackend; import com.redhat.thermostat.thread.harvester.ThreadHarvester; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; public class Activator implements BundleActivator { private ScheduledExecutorService executor = Executors.newScheduledThreadPool(24); + private ServiceTracker connectionPoolTracker; private ServiceTracker threadDaoTracker; private ServiceRegistration backendRegistration; @@ -67,7 +69,21 @@ @Override public void start(final BundleContext context) throws Exception { - harvester = new ThreadHarvester(executor); + connectionPoolTracker = new ServiceTracker(context, MXBeanConnectionPool.class, null) { + @Override + public Object addingService(ServiceReference reference) { + MXBeanConnectionPool pool = (MXBeanConnectionPool) super.addingService(reference); + harvester = new ThreadHarvester(executor, pool); + return pool; + } + @Override + public void removedService(ServiceReference reference, Object service) { + super.removedService(reference, service); + harvester = null; + } + }; + connectionPoolTracker.open(); + registry = new ReceiverRegistry(context); VmStatusListenerRegistrar vmListener = new VmStatusListenerRegistrar(context); @@ -89,7 +105,9 @@ @Override public void removedService(ServiceReference reference, Object service) { - harvester.setThreadDao(null); + if (harvester != null) { + harvester.setThreadDao(null); + } context.ungetService(reference); super.removedService(reference, service); } @@ -105,6 +123,7 @@ backendRegistration.unregister(); + connectionPoolTracker.close(); threadDaoTracker.close(); if (executor != null) { diff -r 0e9e22f2696a -r 00c6bf165dce thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java --- a/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java Mon Apr 29 11:54:04 2013 -0400 +++ b/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java Thu May 02 16:21:24 2013 -0400 @@ -61,12 +61,11 @@ import org.mockito.ArgumentCaptor; import com.redhat.thermostat.thread.dao.ThreadDao; - import com.redhat.thermostat.thread.model.ThreadInfoData; import com.redhat.thermostat.thread.model.ThreadSummary; import com.redhat.thermostat.thread.model.VMThreadCapabilities; import com.redhat.thermostat.utils.management.MXBeanConnection; -import com.redhat.thermostat.utils.management.MXBeanConnector; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; public class HarvesterTest { @@ -74,9 +73,8 @@ public void testStart() { ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ThreadDao dao = mock(ThreadDao.class); - final MXBeanConnector mockedConnector = mock(MXBeanConnector.class); - when(mockedConnector.isAttached()).thenReturn(false); - + MXBeanConnectionPool pool = mock(MXBeanConnectionPool.class); + ArgumentCaptor arg0 = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor arg1 = ArgumentCaptor.forClass(Long.class); ArgumentCaptor arg2 = ArgumentCaptor.forClass(Long.class); @@ -86,8 +84,7 @@ when(executor.scheduleAtFixedRate(arg0.capture(), arg1.capture(), arg2.capture(), arg3.capture())).thenReturn(null); - Harvester harvester = new Harvester(dao, executor, "42") { - { connector = mockedConnector; } + Harvester harvester = new Harvester(dao, executor, "42", pool) { @Override synchronized void harvestData() { harvestDataCalled[0] = true; @@ -120,8 +117,7 @@ ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ThreadDao dao = mock(ThreadDao.class); - final MXBeanConnector mockedConnector = mock(MXBeanConnector.class); - when(mockedConnector.isAttached()).thenReturn(false); + MXBeanConnectionPool pool = mock(MXBeanConnectionPool.class); ArgumentCaptor arg0 = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor arg1 = ArgumentCaptor.forClass(Long.class); @@ -132,8 +128,7 @@ when(executor.scheduleAtFixedRate(arg0.capture(), arg1.capture(), arg2.capture(), arg3.capture())).thenReturn(null); - Harvester harvester = new Harvester(dao, executor, "42") { - { connector = mockedConnector; } + Harvester harvester = new Harvester(dao, executor, "42", pool) { @Override synchronized void harvestData() { harvestDataCalled[0] = true; @@ -143,8 +138,6 @@ harvester.start(); harvester.start(); - verify(mockedConnector, times(1)).isAttached(); - verify(mockedConnector, times(1)).attach(); verify(executor, times(1)).scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class)); assertTrue(arg1.getValue() == 0); @@ -169,18 +162,13 @@ ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ThreadDao dao = mock(ThreadDao.class); - final MXBeanConnector mockedConnector = mock(MXBeanConnector.class); - + MXBeanConnectionPool pool = mock(MXBeanConnectionPool.class); MXBeanConnection connection = mock(MXBeanConnection.class); - - when(mockedConnector.connect()).thenReturn(connection); - - when(mockedConnector.isAttached()).thenReturn(true); + when(pool.acquire(42)).thenReturn(connection); when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future); - Harvester harvester = new Harvester(dao, executor, "42") - {{ connector = mockedConnector; }}; + Harvester harvester = new Harvester(dao, executor, "42", pool); harvester.start(); @@ -189,11 +177,9 @@ harvester.stop(); verify(future).cancel(false); - verify(connection).close(); - // needs to be 2 times, since is called once in start - verify(mockedConnector, times(2)).isAttached(); - verify(mockedConnector).close(); + verify(pool).acquire(42); + verify(pool).release(42, connection); assertFalse(harvester.isConnected()); } @@ -210,18 +196,13 @@ ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ThreadDao dao = mock(ThreadDao.class); - final MXBeanConnector mockedConnector = mock(MXBeanConnector.class); - + MXBeanConnectionPool pool = mock(MXBeanConnectionPool.class); MXBeanConnection connection = mock(MXBeanConnection.class); - - when(mockedConnector.connect()).thenReturn(connection); - - when(mockedConnector.isAttached()).thenReturn(true); + when(pool.acquire(42)).thenReturn(connection); when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future); - Harvester harvester = new Harvester(dao, executor, "42") - {{ connector = mockedConnector; }}; + Harvester harvester = new Harvester(dao, executor, "42", pool); harvester.start(); @@ -231,12 +212,9 @@ harvester.stop(); verify(future, times(1)).cancel(false); - verify(connection, times(1)).close(); - // needs to be 2 times, since is called once in start - verify(mockedConnector, times(2)).isAttached(); - - verify(mockedConnector, times(1)).close(); + verify(pool).acquire(42); + verify(pool).release(42, connection); assertFalse(harvester.isConnected()); } @@ -250,13 +228,9 @@ ScheduledFuture future = mock(ScheduledFuture.class); when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future); - final MXBeanConnector mockedConnector = mock(MXBeanConnector.class); - MXBeanConnection connection = mock(MXBeanConnection.class); - when(mockedConnector.connect()).thenReturn(connection); - when(mockedConnector.isAttached()).thenReturn(true); - - Harvester harvester = new Harvester(dao, executor, "42") - {{ connector = mockedConnector; }}; + MXBeanConnectionPool pool = mock(MXBeanConnectionPool.class); + + Harvester harvester = new Harvester(dao, executor, "42", pool); verify(executor, times(0)).scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class)); @@ -265,7 +239,6 @@ harvester.stop(); assertFalse(harvester.isConnected()); - verify(mockedConnector, times(0)).isAttached(); verify(future, times(0)).cancel(false); } @@ -291,7 +264,8 @@ info1, info2 }; - + + MXBeanConnectionPool pool = mock(MXBeanConnectionPool.class); ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ArgumentCaptor summaryCapture = ArgumentCaptor.forClass(ThreadSummary.class); @@ -310,7 +284,7 @@ final boolean [] getDataCollectorBeanCalled = new boolean[1]; - Harvester harvester = new Harvester(dao, executor, "42") { + Harvester harvester = new Harvester(dao, executor, "42", pool) { @Override ThreadMXBean getDataCollectorBean(MXBeanConnection connection) throws MalformedObjectNameException { @@ -354,8 +328,7 @@ @Test public void testSaveVmCaps() { - final MXBeanConnector mockedConnector = mock(MXBeanConnector.class); - when(mockedConnector.isAttached()).thenReturn(true); + MXBeanConnectionPool pool = mock(MXBeanConnectionPool.class); ScheduledExecutorService executor = mock(ScheduledExecutorService.class); @@ -370,8 +343,7 @@ final boolean [] getDataCollectorBeanCalled = new boolean[1]; - Harvester harvester = new Harvester(dao, executor, "42") { - { connector = mockedConnector; } + Harvester harvester = new Harvester(dao, executor, "42", pool) { @Override ThreadMXBean getDataCollectorBean(MXBeanConnection connection) throws MalformedObjectNameException { diff -r 0e9e22f2696a -r 00c6bf165dce thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java --- a/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java Mon Apr 29 11:54:04 2013 -0400 +++ b/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java Thu May 02 16:21:24 2013 -0400 @@ -45,6 +45,7 @@ import java.util.List; import java.util.concurrent.ScheduledExecutorService; +import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -55,12 +56,21 @@ import com.redhat.thermostat.thread.collector.HarvesterCommand; import com.redhat.thermostat.thread.dao.ThreadDao; import com.redhat.thermostat.thread.model.ThreadHarvestingStatus; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; public class ThreadHarvesterTest { + private MXBeanConnectionPool pool; + private ScheduledExecutorService executor; + + @Before + public void setUp() { + pool = mock(MXBeanConnectionPool.class); + executor = mock(ScheduledExecutorService.class); + } + @Test public void testStart() { - ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ThreadDao dao = mock(ThreadDao.class); Request request = mock(Request.class); @@ -75,7 +85,7 @@ thenReturn("42"). thenReturn("0xcafe"); - ThreadHarvester threadHarvester = new ThreadHarvester(executor) { + ThreadHarvester threadHarvester = new ThreadHarvester(executor, pool) { @Override Harvester createHarvester(String vmId) { @@ -101,7 +111,6 @@ @Test public void testStop() { - ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ThreadDao dao = mock(ThreadDao.class); Request request = mock(Request.class); @@ -113,7 +122,7 @@ thenReturn(HarvesterCommand.STOP.name()). thenReturn("42"); - ThreadHarvester threadHarvester = new ThreadHarvester(executor) { + ThreadHarvester threadHarvester = new ThreadHarvester(executor, pool) { { connectors.put("42", harverster); } }; threadHarvester.setThreadDao(dao); @@ -130,13 +139,12 @@ @Test public void testSaveVmCaps() { - ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ThreadDao dao = mock(ThreadDao.class); final boolean[] createHarvesterCalled = new boolean[1]; final Harvester harverster = mock(Harvester.class); - ThreadHarvester threadHarvester = new ThreadHarvester(executor) { + ThreadHarvester threadHarvester = new ThreadHarvester(executor, pool) { @Override Harvester createHarvester(String vmId) { @@ -156,9 +164,7 @@ @Test public void testRecieveWithoutDaosFails() { - ScheduledExecutorService executor = mock(ScheduledExecutorService.class); - - ThreadHarvester harvester = new ThreadHarvester(executor); + ThreadHarvester harvester = new ThreadHarvester(executor, pool); Response response = harvester.receive(mock(Request.class)); assertEquals(ResponseType.ERROR, response.getType()); @@ -166,12 +172,11 @@ @Test public void testHarvestingStatus() { - ScheduledExecutorService executor = mock(ScheduledExecutorService.class); Clock clock = mock(Clock.class); when(clock.getRealTimeMillis()).thenReturn(1l); ThreadDao dao = mock(ThreadDao.class); - ThreadHarvester harvester = new ThreadHarvester(executor, clock); + ThreadHarvester harvester = new ThreadHarvester(executor, clock, pool); harvester.setThreadDao(dao); harvester.addThreadHarvestingStatus("10"); @@ -187,12 +192,11 @@ @Test public void testHarvestingStatusAfterSavingVmCaps() { - ScheduledExecutorService executor = mock(ScheduledExecutorService.class); Clock clock = mock(Clock.class); when(clock.getRealTimeMillis()).thenReturn(1l); ThreadDao dao = mock(ThreadDao.class); - ThreadHarvester harvester = new ThreadHarvester(executor, clock); + ThreadHarvester harvester = new ThreadHarvester(executor, clock, pool); harvester.setThreadDao(dao); harvester.saveVmCaps("10"); @@ -209,7 +213,6 @@ @Test public void testStopAndRemoveAll() { - ScheduledExecutorService executor = mock(ScheduledExecutorService.class); Clock clock = mock(Clock.class); when(clock.getRealTimeMillis()).thenReturn(1l); ThreadDao dao = mock(ThreadDao.class); @@ -219,7 +222,7 @@ when(javaHarvester.start()).thenReturn(true); when(javaHarvester.stop()).thenReturn(true); - ThreadHarvester harvester = new ThreadHarvester(executor, clock) { + ThreadHarvester harvester = new ThreadHarvester(executor, clock, pool) { @Override Harvester createHarvester(String vmId) { diff -r 0e9e22f2696a -r 00c6bf165dce vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCCommandReceiver.java --- a/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCCommandReceiver.java Mon Apr 29 11:54:04 2013 -0400 +++ b/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCCommandReceiver.java Thu May 02 16:21:24 2013 -0400 @@ -43,10 +43,17 @@ import com.redhat.thermostat.gc.remote.command.internal.GC; import com.redhat.thermostat.gc.remote.command.internal.GCException; import com.redhat.thermostat.gc.remote.common.command.GCCommand; -import com.redhat.thermostat.utils.management.MXBeanConnector; +import com.redhat.thermostat.utils.management.MXBeanConnection; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; public class GCCommandReceiver implements RequestReceiver { + private MXBeanConnectionPool pool; + + public GCCommandReceiver(MXBeanConnectionPool pool) { + this.pool = pool; + } + @Override public Response receive(Request request) { Response response = new Response(ResponseType.OK); @@ -54,10 +61,9 @@ String command = request.getParameter(GCCommand.class.getName()); switch (GCCommand.valueOf(command)) { case REQUEST_GC: + String vmId = request.getParameter(GCCommand.VM_ID); try { - String vmId = request.getParameter(GCCommand.VM_ID); - MXBeanConnector connector = new MXBeanConnector(vmId); - new GC(connector).gc(); + new GC(pool, vmId).gc(); } catch (GCException gce) { response = new Response(ResponseType.ERROR); } diff -r 0e9e22f2696a -r 00c6bf165dce vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/GC.java --- a/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/GC.java Mon Apr 29 11:54:04 2013 -0400 +++ b/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/GC.java Thu May 02 16:21:24 2013 -0400 @@ -43,58 +43,42 @@ import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.utils.management.MXBeanConnection; -import com.redhat.thermostat.utils.management.MXBeanConnector; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; public class GC { private static final Logger logger = LoggingUtils.getLogger(GC.class); - private MXBeanConnector connector; - public GC(MXBeanConnector connector) { - this.connector = connector; + private MXBeanConnectionPool pool; + private int vmId; + + public GC(MXBeanConnectionPool pool, String vmId) { + this.pool = pool; + this.vmId = Integer.valueOf(vmId); } - + public void gc() throws GCException { + Exception exceptionInGc = null; - boolean closeAfter = false; - if (!connector.isAttached()) { - closeAfter = true; + + try { + MXBeanConnection connection = pool.acquire(vmId); try { - connector.attach(); + MemoryMXBean bean = connection.createProxy(ManagementFactory.MEMORY_MXBEAN_NAME, MemoryMXBean.class); + bean.gc(); } catch (Exception ex) { - logger.log(Level.SEVERE, "can't attach", ex); - if (closeAfter) { - closeConnection(); - } + exceptionInGc = ex; + logger.log(Level.SEVERE, "can't get MXBeanConnection connection", ex); + } finally { + pool.release(vmId, connection); } - } - - try (MXBeanConnection connection = connector.connect()) { - - MemoryMXBean bean = connection.createProxy(ManagementFactory.MEMORY_MXBEAN_NAME, MemoryMXBean.class); - bean.gc(); - - } catch (Exception ex) { - exceptionInGc = ex; - logger.log(Level.SEVERE, "can't get MXBeanConnection connection", ex); - } - - if (closeAfter) { - closeConnection(); + } catch (Exception ioe) { + exceptionInGc = ioe; } if (exceptionInGc != null) { throw new GCException("error performing gc", exceptionInGc); } } - - private void closeConnection() { - try { - connector.close(); - } catch (Exception ex) { - logger.log(Level.SEVERE, "can't close connection to vm", ex); - } - } } - diff -r 0e9e22f2696a -r 00c6bf165dce vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/osgi/GCCommandReceiverActivator.java --- a/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/osgi/GCCommandReceiverActivator.java Mon Apr 29 11:54:04 2013 -0400 +++ b/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/osgi/GCCommandReceiverActivator.java Thu May 02 16:21:24 2013 -0400 @@ -38,20 +38,38 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; import com.redhat.thermostat.agent.command.ReceiverRegistry; import com.redhat.thermostat.gc.remote.command.GCCommandReceiver; +import com.redhat.thermostat.utils.management.MXBeanConnectionPool; public class GCCommandReceiverActivator implements BundleActivator { + ServiceTracker tracker; + @Override public void start(BundleContext context) throws Exception { - ReceiverRegistry registry = new ReceiverRegistry(context); - registry.registerReceiver(new GCCommandReceiver()); + final ReceiverRegistry registry = new ReceiverRegistry(context); + + tracker = new ServiceTracker(context, MXBeanConnectionPool.class, null) { + public MXBeanConnectionPool addingService(ServiceReference reference) { + MXBeanConnectionPool pool = (MXBeanConnectionPool) super.addingService(reference); + registry.registerReceiver(new GCCommandReceiver(pool)); + return pool; + }; + + public void removedService(org.osgi.framework.ServiceReference reference, Object service) { + registry.unregisterReceivers(); + super.removedService(reference, service); + }; + }; + tracker.open(); } - + @Override public void stop(BundleContext context) throws Exception { + tracker.close(); } } -