changeset 1077:00c6bf165dce

Connection pool for JMX connections Reviewed-by: neugens, ebaron Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-April/006456.html
author Omair Majid <omajid@redhat.com>
date Thu, 02 May 2013 16:21:24 -0400
parents 0e9e22f2696a
children 7f100b629033
files agent/core/pom.xml agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnectionPool.java agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnector.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionImpl.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImpl.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnector.java agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImplTest.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/osgi/Activator.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCCommandReceiver.java vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/GC.java vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/osgi/GCCommandReceiverActivator.java
diffstat 18 files changed, 786 insertions(+), 354 deletions(-) [+]
line wrap: on
line diff
--- 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 @@
           <instructions>
             <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
             <Bundle-SymbolicName>com.redhat.thermostat.agent.core</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.agent.internal.Activator</Bundle-Activator>
             <Export-Package>
               com.redhat.thermostat.agent,
               com.redhat.thermostat.agent.config,
@@ -120,7 +121,9 @@
               com.redhat.thermostat.utils.management,
             </Export-Package>
             <Private-Package>
+              com.redhat.thermostat.agent.internal,
               com.redhat.thermostat.backend.internal,
+              com.redhat.thermostat.utils.management.internal,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.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();
+    }
+
+}
--- 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> E createProxy(String name, Class<? extends E> proxyClass) throws MalformedObjectNameException {
-        ObjectName objectName = new ObjectName(name);
-        return JMX.newMXBeanProxy(mbsc, objectName, proxyClass);
-    }
-    
-    @Override
-    public void close() throws IOException {
-        connection.close();
-    }
+    <E> E createProxy(String name, Class<? extends E> proxyClass) throws MalformedObjectNameException;
+
 }
-
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.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;
+}
--- 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
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.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;
-        }
-    }
-}
-
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.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> E createProxy(String name, Class<? extends E> proxyClass) throws MalformedObjectNameException {
+        ObjectName objectName = new ObjectName(name);
+        return JMX.newMXBeanProxy(mbsc, objectName, proxyClass);
+    }
+    
+    void close() throws IOException {
+        connection.close();
+    }
+}
+
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.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<Integer, Pair<Integer, MXBeanConnectionImpl>> 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<Integer, MXBeanConnectionImpl> data = pool.get(pid);
+        if (data == null) {
+            MXBeanConnector connector = creator.create(pid);
+            connector.attach();
+            MXBeanConnectionImpl connection = connector.connect();
+            connector.close();
+            data = new Pair<Integer, MXBeanConnectionImpl>(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<Integer, MXBeanConnectionImpl> 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));
+        }
+    }
+}
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.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;
+        }
+    }
+}
+
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.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));
+    }
+}
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.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();
+
+    }
+}
--- 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<String> 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<String> 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;
     }
 }
 
--- 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);
     }
 
     /**
--- 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) {
--- 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<Runnable> arg0 = ArgumentCaptor.forClass(Runnable.class);
         ArgumentCaptor<Long> arg1 = ArgumentCaptor.forClass(Long.class);
         ArgumentCaptor<Long> 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<Runnable> arg0 = ArgumentCaptor.forClass(Runnable.class);
         ArgumentCaptor<Long> 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<ThreadSummary> 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 {
--- 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) {
 
--- 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);
             }
--- 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);
-        }
-    }    
 }
-
--- 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();
     }
 }
-