You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2018/10/29 08:41:52 UTC

[incubator-plc4x] branch master updated: [plc4j-pool] use native KeyedObjectPool and ditch custom implementation Custom keys per protocol are still needed

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git


The following commit(s) were added to refs/heads/master by this push:
     new 4e440a9  [plc4j-pool] use native KeyedObjectPool and ditch custom implementation Custom keys per protocol are still needed
4e440a9 is described below

commit 4e440a942ee34e659c0916ca630cf451e62f73d4
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Mon Oct 29 09:38:46 2018 +0100

    [plc4j-pool] use native KeyedObjectPool and ditch custom implementation
    Custom keys per protocol are still needed
---
 .../plc4x/java/utils/connectionpool/PoolKey.java   |  74 ++++++++
 .../connectionpool/PooledPlcConnectionFactory.java |   8 +-
 .../connectionpool/PooledPlcDriverManager.java     | 198 ++++++---------------
 .../connectionpool/PooledPlcDriverManagerTest.java | 110 +++---------
 4 files changed, 156 insertions(+), 234 deletions(-)

diff --git a/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKey.java b/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKey.java
new file mode 100644
index 0000000..166589c
--- /dev/null
+++ b/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKey.java
@@ -0,0 +1,74 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package org.apache.plc4x.java.utils.connectionpool;
+
+import org.apache.plc4x.java.api.authentication.PlcAuthentication;
+
+import java.util.Objects;
+
+public class PoolKey {
+    final String url;
+    final PlcAuthentication plcAuthentication;
+
+    // TODO: we need to extract relevant parts of the url as key as we don't want many connections for different racks in s7 for example.
+    // TODO: So we might end up need a generic key and special keys for all known protocols which parses the relevant portions.
+    public PoolKey(String url, PlcAuthentication plcAuthentication) {
+        this.url = url;
+        this.plcAuthentication = plcAuthentication;
+    }
+
+    public static PoolKey of(String host, PlcAuthentication plcAuthentication) {
+        return new PoolKey(host, plcAuthentication);
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public PlcAuthentication getPlcAuthentication() {
+        return plcAuthentication;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof PoolKey)) {
+            return false;
+        }
+        PoolKey poolKey = (PoolKey) o;
+        return Objects.equals(url, poolKey.url) &&
+            Objects.equals(plcAuthentication, poolKey.plcAuthentication);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(url, plcAuthentication);
+    }
+
+    @Override
+    public String toString() {
+        return "PoolKey{" +
+            "url='" + url + '\'' +
+            (plcAuthentication != PooledPlcDriverManager.noPlcAuthentication ? ", plcAuthentication=" + plcAuthentication : "") +
+            '}';
+    }
+}
\ No newline at end of file
diff --git a/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcConnectionFactory.java b/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcConnectionFactory.java
index 72d3deb..03879eb 100644
--- a/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcConnectionFactory.java
+++ b/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcConnectionFactory.java
@@ -17,14 +17,14 @@
 
 package org.apache.plc4x.java.utils.connectionpool;
 
-import org.apache.commons.pool2.BasePooledObjectFactory;
+import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
 import org.apache.commons.pool2.PooledObject;
 import org.apache.commons.pool2.impl.DefaultPooledObject;
 import org.apache.plc4x.java.api.PlcConnection;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public abstract class PooledPlcConnectionFactory extends BasePooledObjectFactory<PlcConnection> {
+public abstract class PooledPlcConnectionFactory extends BaseKeyedPooledObjectFactory<PoolKey, PlcConnection> {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(PooledPlcConnectionFactory.class);
 
@@ -35,12 +35,12 @@ public abstract class PooledPlcConnectionFactory extends BasePooledObjectFactory
     }
 
     @Override
-    public void destroyObject(PooledObject<PlcConnection> p) throws Exception {
+    public void destroyObject(PoolKey key, PooledObject<PlcConnection> p) throws Exception {
         p.getObject().close();
     }
 
     @Override
-    public boolean validateObject(PooledObject<PlcConnection> p) {
+    public boolean validateObject(PoolKey key, PooledObject<PlcConnection> p) {
         return p.getObject().isConnected();
     }
 }
diff --git a/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManager.java b/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManager.java
index b0f9701..d10dc20 100644
--- a/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManager.java
+++ b/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManager.java
@@ -17,54 +17,65 @@
 
 package org.apache.plc4x.java.utils.connectionpool;
 
-import org.apache.commons.pool2.ObjectPool;
-import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.commons.pool2.KeyedObjectPool;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
 import org.apache.plc4x.java.PlcDriverManager;
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.authentication.PlcAuthentication;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 public class PooledPlcDriverManager extends PlcDriverManager {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(PooledPlcDriverManager.class);
 
-    private PoolCreator poolCreator;
-
-    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
-
-    private ConcurrentMap<PoolKey, ObjectPool<PlcConnection>> poolMap = new ConcurrentHashMap<>();
+    private KeyedObjectPool<PoolKey, PlcConnection> keyedObjectPool;
 
     // Marker class do detected a non null value
-    private static final NoPlcAuthentication noPlcAuthentication = new NoPlcAuthentication();
+    static final NoPlcAuthentication noPlcAuthentication = new NoPlcAuthentication();
 
     public PooledPlcDriverManager() {
-        this(GenericObjectPool::new);
+        this(GenericKeyedObjectPool::new);
     }
 
     public PooledPlcDriverManager(ClassLoader classLoader) {
         super(classLoader);
-        this.poolCreator = GenericObjectPool::new;
+        setFromPoolCreator(GenericKeyedObjectPool::new);
     }
 
     public PooledPlcDriverManager(PoolCreator poolCreator) {
-        this.poolCreator = poolCreator;
+        setFromPoolCreator(poolCreator);
     }
 
     public PooledPlcDriverManager(ClassLoader classLoader, PoolCreator poolCreator) {
         super(classLoader);
-        this.poolCreator = poolCreator;
+        setFromPoolCreator(poolCreator);
+    }
+
+    private void setFromPoolCreator(PoolCreator poolCreator) {
+        this.keyedObjectPool = poolCreator.createPool(new PooledPlcConnectionFactory() {
+            @Override
+            public PlcConnection create(PoolKey key) throws Exception {
+                PlcAuthentication plcAuthentication = key.plcAuthentication;
+                String url = key.url;
+                if (plcAuthentication == noPlcAuthentication) {
+                    LOGGER.debug("getting actual connection for {}", url);
+                    return PooledPlcDriverManager.super.getConnection(url);
+                } else {
+                    LOGGER.debug("getting actual connection for {} and plcAuthentication {}", url, plcAuthentication);
+                    return PooledPlcDriverManager.super.getConnection(url, plcAuthentication);
+                }
+            }
+        });
     }
 
     @Override
@@ -75,7 +86,6 @@ public class PooledPlcDriverManager extends PlcDriverManager {
     @Override
     public PlcConnection getConnection(String url, PlcAuthentication authentication) throws PlcConnectionException {
         PoolKey poolKey = PoolKey.of(url, authentication);
-        ObjectPool<PlcConnection> pool = retrieveFromPool(poolKey);
         if (LOGGER.isDebugEnabled()) {
             if (authentication != noPlcAuthentication) {
                 LOGGER.debug("Try to borrow an object for url {} and authentication {}", url, authentication);
@@ -85,7 +95,7 @@ public class PooledPlcDriverManager extends PlcDriverManager {
         }
         PlcConnection plcConnection;
         try {
-            plcConnection = pool.borrowObject();
+            plcConnection = keyedObjectPool.borrowObject(poolKey);
         } catch (Exception e) {
             throw new PlcConnectionException(e);
         }
@@ -96,16 +106,16 @@ public class PooledPlcDriverManager extends PlcDriverManager {
                 throw new IllegalStateException("Proxy not valid anymore");
             }
             if ("close".equals(method.getName())) {
-                LOGGER.debug("close called on {}. Returning to {}", plcConnection, pool);
+                LOGGER.debug("close called on {}", plcConnection);
                 proxyInvalidated.set(true);
-                pool.returnObject(plcConnection);
+                keyedObjectPool.returnObject(poolKey, plcConnection);
                 return null;
             } else {
                 try {
                     return method.invoke(plcConnection, args);
                 } catch (InvocationTargetException e) {
                     if (e.getCause().getClass() == PlcConnectionException.class) {
-                        pool.invalidateObject(plcConnection);
+                        keyedObjectPool.invalidateObject(poolKey, plcConnection);
                         proxyInvalidated.set(true);
                     }
                     throw e;
@@ -114,139 +124,35 @@ public class PooledPlcDriverManager extends PlcDriverManager {
         });
     }
 
-    private ObjectPool<PlcConnection> retrieveFromPool(PoolKey poolKey) {
-        ObjectPool<PlcConnection> pool = poolMap.get(poolKey);
-        if (pool == null) {
-            LOGGER.debug("No pool found for poolKey {}", poolKey);
-            String url = poolKey.getUrl();
-            PlcAuthentication plcAuthentication = poolKey.getPlcAuthentication();
-
-            Lock lock = readWriteLock.writeLock();
-            lock.lock();
-            try {
-                pool = poolMap.computeIfAbsent(poolKey, pair -> poolCreator.createPool(new PooledPlcConnectionFactory() {
-                    @Override
-                    public PlcConnection create() throws PlcConnectionException {
-                        if (plcAuthentication == noPlcAuthentication) {
-                            LOGGER.debug("getting actual connection for {}", url);
-                            return PooledPlcDriverManager.super.getConnection(url);
-                        } else {
-                            LOGGER.debug("getting actual connection for {} and plcAuthentication {}", url, plcAuthentication);
-                            return PooledPlcDriverManager.super.getConnection(url, plcAuthentication);
-                        }
-                    }
-                }));
-                LOGGER.debug("Using pool {}:{} for poolKey {}", pool.hashCode(), pool, poolKey);
-            } finally {
-                lock.unlock();
-            }
-        }
-        return pool;
-    }
-
     @FunctionalInterface
     interface PoolCreator {
-        ObjectPool<PlcConnection> createPool(PooledPlcConnectionFactory pooledPlcConnectionFactory);
-    }
-
-    // TODO: maybe add a Thread which calls this cyclic
-    public void removedUnusedPools() {
-        Lock lock = readWriteLock.writeLock();
-        lock.lock();
-        try {
-            LOGGER.debug("doing cleanup now on {}", poolMap);
-            Set<PoolKey> itemsToBeremoved = new LinkedHashSet<>();
-            poolMap.forEach((key, value) -> {
-                // TODO: check if this pool has been used in the last time and if not remove it.
-                // TODO: evicting empty pools for now
-                LOGGER.debug("pool {}:{} has numActive: {} numIdle: {}", key, value, value.getNumActive(), value.getNumIdle());
-                if (value.getNumActive() == 0 && value.getNumIdle() == 0) {
-                    LOGGER.info("Removing unused pool {}", value);
-                    itemsToBeremoved.add(key);
-                }
-            });
-            LOGGER.debug("items to be removed {} on {}", itemsToBeremoved, poolMap);
-            itemsToBeremoved.forEach(poolMap::remove);
-        } finally {
-            lock.unlock();
-        }
+        KeyedObjectPool<PoolKey, PlcConnection> createPool(PooledPlcConnectionFactory pooledPlcConnectionFactory);
     }
 
-    // TODO: maybe export to jmx
+    // TODO: maybe export to jmx // generic poolKey has builting jmx too
     public Map<String, Number> getStatistics() {
-        Lock lock = readWriteLock.readLock();
-        try {
-            lock.lock();
-            HashMap<String, Number> statistics = new HashMap<>();
-            statistics.put("pools.count", poolMap.size());
-            for (Map.Entry<PoolKey, ObjectPool<PlcConnection>> poolEntry : poolMap.entrySet()) {
-                PoolKey pair = poolEntry.getKey();
-                ObjectPool<PlcConnection> objectPool = poolEntry.getValue();
-                String url = pair.getUrl();
-                PlcAuthentication plcAuthentication = pair.getPlcAuthentication();
-
-                String authSuffix = plcAuthentication != noPlcAuthentication ? "/" + plcAuthentication : "";
-                statistics.put(url + authSuffix + ".numActive", objectPool.getNumActive());
-                statistics.put(url + authSuffix + ".numIdle", objectPool.getNumIdle());
+        HashMap<String, Number> statistics = new HashMap<>();
+        statistics.put("numActive", keyedObjectPool.getNumActive());
+        statistics.put("numIdle", keyedObjectPool.getNumIdle());
+        if (keyedObjectPool instanceof GenericKeyedObjectPool) {
+            GenericKeyedObjectPool<PoolKey, PlcConnection> genericKeyedObjectPool = (GenericKeyedObjectPool<PoolKey, PlcConnection>) this.keyedObjectPool;
+            // Thats pretty ugly and we really should't do that...
+            try {
+                Map poolMap = (Map) FieldUtils.getField(GenericKeyedObjectPool.class, "poolMap", true).get(this.keyedObjectPool);
+                statistics.put("pools.count", poolMap.size());
+            } catch (IllegalAccessException e) {
+                throw new PlcRuntimeException(e);
+            }
+            Map<String, Integer> numActivePerKey = genericKeyedObjectPool.getNumActivePerKey();
+            for (Map.Entry<String, Integer> entry : numActivePerKey.entrySet()) {
+                statistics.put(entry.getKey() + ".numActive", entry.getValue());
             }
-
-            return statistics;
-        } finally {
-            lock.unlock();
         }
-    }
-
-    private static final class NoPlcAuthentication implements PlcAuthentication {
 
+        return statistics;
     }
 
-    private static final class PoolKey {
-        final String url;
-        final PlcAuthentication plcAuthentication;
-
-        // TODO: we need to extract relevant parts of the url as key as we don't want many connections for different racks in s7 for example.
-        // TODO: So we might end up need a generic key and special keys for all known protocols which parses the relevant portions.
-        public PoolKey(String url, PlcAuthentication plcAuthentication) {
-            this.url = url;
-            this.plcAuthentication = plcAuthentication;
-        }
-
-        public static PoolKey of(String host, PlcAuthentication plcAuthentication) {
-            return new PoolKey(host, plcAuthentication);
-        }
-
-        public String getUrl() {
-            return url;
-        }
-
-        public PlcAuthentication getPlcAuthentication() {
-            return plcAuthentication;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (!(o instanceof PoolKey)) {
-                return false;
-            }
-            PoolKey poolKey = (PoolKey) o;
-            return Objects.equals(url, poolKey.url) &&
-                Objects.equals(plcAuthentication, poolKey.plcAuthentication);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(url, plcAuthentication);
-        }
+    private static final class NoPlcAuthentication implements PlcAuthentication {
 
-        @Override
-        public String toString() {
-            return "PoolKey{" +
-                "url='" + url + '\'' +
-                (plcAuthentication != noPlcAuthentication ? ", plcAuthentication=" + plcAuthentication : "") +
-                '}';
-        }
     }
 }
diff --git a/plc4j/utils/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java b/plc4j/utils/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java
index 9ab3972..bbe8ea8 100644
--- a/plc4j/utils/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java
+++ b/plc4j/utils/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java
@@ -18,8 +18,8 @@
 package org.apache.plc4x.java.utils.connectionpool;
 
 import org.apache.commons.lang3.reflect.FieldUtils;
-import org.apache.commons.pool2.impl.GenericObjectPool;
-import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.authentication.PlcAuthentication;
 import org.apache.plc4x.java.api.authentication.PlcUsernamePasswordAuthentication;
@@ -34,7 +34,6 @@ import org.apache.plc4x.java.spi.PlcDriver;
 import org.assertj.core.api.WithAssertions;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Answers;
@@ -48,7 +47,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.IntStream;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -61,11 +59,11 @@ class PooledPlcDriverManagerTest implements WithAssertions {
     private static Logger LOGGER = LoggerFactory.getLogger(PooledPlcDriverManagerTest.class);
 
     private PooledPlcDriverManager SUT = new PooledPlcDriverManager(pooledPlcConnectionFactory -> {
-        GenericObjectPoolConfig<PlcConnection> plcConnectionGenericObjectPoolConfig = new GenericObjectPoolConfig<>();
-        plcConnectionGenericObjectPoolConfig.setMinIdle(1);
-        plcConnectionGenericObjectPoolConfig.setTestOnBorrow(true);
-        plcConnectionGenericObjectPoolConfig.setTestOnReturn(true);
-        return new GenericObjectPool<>(pooledPlcConnectionFactory, plcConnectionGenericObjectPoolConfig);
+        GenericKeyedObjectPoolConfig<PlcConnection> config = new GenericKeyedObjectPoolConfig<>();
+        config.setMinIdlePerKey(1);
+        config.setTestOnBorrow(true);
+        config.setTestOnReturn(true);
+        return new GenericKeyedObjectPool<>(pooledPlcConnectionFactory, config);
     });
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -80,7 +78,11 @@ class PooledPlcDriverManagerTest implements WithAssertions {
         driverMap.put("dummydummy", plcDriver);
         executorService = Executors.newFixedThreadPool(100);
 
-        assertThat(SUT.getStatistics()).containsOnly(entry("pools.count", 0));
+        assertThat(SUT.getStatistics()).containsOnly(
+            entry("pools.count", 0),
+            entry("numActive", 0),
+            entry("numIdle", 0)
+        );
     }
 
     @AfterEach
@@ -128,8 +130,7 @@ class PooledPlcDriverManagerTest implements WithAssertions {
         verify(plcDriver, times(13)).connect(anyString());
 
         assertThat(SUT.getStatistics()).contains(
-            entry("dummydummy:single/socket1/socket2?fancyOption=true.numActive", 8),
-            entry("dummydummy:single/socket1/socket2?fancyOption=true.numIdle", 0)
+            entry("PoolKey{url='dummydummy:single/socket1/socket2?fancyOption=true'}.numActive", 8)
         );
 
         futures.forEach(plcConnectionFuture -> {
@@ -141,8 +142,7 @@ class PooledPlcDriverManagerTest implements WithAssertions {
         });
 
         assertThat(SUT.getStatistics()).contains(
-            entry("dummydummy:single/socket1/socket2?fancyOption=true.numActive", 0),
-            entry("dummydummy:single/socket1/socket2?fancyOption=true.numIdle", 8)
+            entry("PoolKey{url='dummydummy:single/socket1/socket2?fancyOption=true'}.numActive", 0)
         );
     }
 
@@ -164,7 +164,7 @@ class PooledPlcDriverManagerTest implements WithAssertions {
         // This should result in five open connections
         IntStream.range(0, 5).forEach(i -> callables.add(() -> {
             try {
-                return SUT.getConnection("dummydummy:single-" + i + "/socket1/socket2?fancyOption=true", new PlcUsernamePasswordAuthentication("user", "passwordp954368564098ß"));
+                return SUT.getConnection("dummydummy:multi-" + i + "/socket1/socket2?fancyOption=true", new PlcUsernamePasswordAuthentication("user", "passwordp954368564098ß"));
             } catch (PlcConnectionException e) {
                 throw new RuntimeException(e);
             }
@@ -186,8 +186,7 @@ class PooledPlcDriverManagerTest implements WithAssertions {
         verify(plcDriver, times(13)).connect(anyString(), any());
 
         assertThat(SUT.getStatistics()).contains(
-            entry("dummydummy:single/socket1/socket2?fancyOption=true/PlcUsernamePasswordAuthentication{username='user', password='*****************'}.numActive", 8),
-            entry("dummydummy:single/socket1/socket2?fancyOption=true/PlcUsernamePasswordAuthentication{username='user', password='*****************'}.numIdle", 0)
+            entry("PoolKey{url='dummydummy:single/socket1/socket2?fancyOption=true', plcAuthentication=PlcUsernamePasswordAuthentication{username='user', password='*****************'}}.numActive", 8)
         );
 
         futures.forEach(plcConnectionFuture -> {
@@ -199,8 +198,7 @@ class PooledPlcDriverManagerTest implements WithAssertions {
         });
 
         assertThat(SUT.getStatistics()).contains(
-            entry("dummydummy:single/socket1/socket2?fancyOption=true/PlcUsernamePasswordAuthentication{username='user', password='*****************'}.numActive", 0),
-            entry("dummydummy:single/socket1/socket2?fancyOption=true/PlcUsernamePasswordAuthentication{username='user', password='*****************'}.numIdle", 8)
+            entry("PoolKey{url='dummydummy:single/socket1/socket2?fancyOption=true', plcAuthentication=PlcUsernamePasswordAuthentication{username='user', password='*****************'}}.numActive", 0)
         );
     }
 
@@ -241,13 +239,16 @@ class PooledPlcDriverManagerTest implements WithAssertions {
         });
 
         assertThat(SUT.getStatistics()).containsOnly(
-            entry("pools.count", 0)
+            entry("pools.count", 0),
+            entry("numActive", 0),
+            entry("numIdle", 0)
         );
         PlcConnection connection = SUT.getConnection("dummydummy:breakIt");
         assertThat(SUT.getStatistics()).containsOnly(
             entry("pools.count", 1),
-            entry("dummydummy:breakIt.numActive", 1),
-            entry("dummydummy:breakIt.numIdle", 0)
+            entry("numActive", 1),
+            entry("numIdle", 0),
+            entry("PoolKey{url='dummydummy:breakIt'}.numActive", 1)
         );
         failNow.set(true);
         try {
@@ -260,71 +261,12 @@ class PooledPlcDriverManagerTest implements WithAssertions {
         }
         // Faulty connection should have been discarded
         assertThat(SUT.getStatistics()).containsOnly(
-            entry("pools.count", 1),
-            entry("dummydummy:breakIt.numActive", 0),
-            entry("dummydummy:breakIt.numIdle", 0)
+            entry("pools.count", 0),
+            entry("numActive", 0),
+            entry("numIdle", 0)
         );
     }
 
-    @Nested
-    class PoolCleanup {
-        @Test
-        void poolRemovedUnusedPoolsNormal() throws Exception {
-            // special config needed
-            SUT = new PooledPlcDriverManager(pooledPlcConnectionFactory -> {
-                GenericObjectPoolConfig<PlcConnection> plcConnectionGenericObjectPoolConfig = new GenericObjectPoolConfig<>();
-                plcConnectionGenericObjectPoolConfig.setMinIdle(1);
-                return new GenericObjectPool<>(pooledPlcConnectionFactory, plcConnectionGenericObjectPoolConfig);
-            });
-            setUp();
-
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 0);
-            SUT.removedUnusedPools();
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 0);
-            PlcConnection connection = SUT.getConnection("dummydummy:single/socket1/socket2?fancyOption=true");
-            connection.close();
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 1);
-            SUT.removedUnusedPools();
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 1);
-            // Usually the removedUnusedPools should do nothing at this place.
-        }
-
-        @Test
-        void poolRemovedUnusedPoolsNoIdles() throws Exception {
-            // special config needed
-            AtomicReference<GenericObjectPool<PlcConnection>> atomicReference = new AtomicReference<>();
-            SUT = new PooledPlcDriverManager(pooledPlcConnectionFactory -> {
-                GenericObjectPoolConfig<PlcConnection> plcConnectionGenericObjectPoolConfig = new GenericObjectPoolConfig<>();
-                plcConnectionGenericObjectPoolConfig.setMinIdle(0);
-                GenericObjectPool<PlcConnection> plcConnectionGenericObjectPool = new GenericObjectPool<>(pooledPlcConnectionFactory, plcConnectionGenericObjectPoolConfig);
-                atomicReference.set(plcConnectionGenericObjectPool);
-                // Evict under all circumstances
-                plcConnectionGenericObjectPool.setEvictionPolicy((config, underTest, idleCount) -> true);
-                return plcConnectionGenericObjectPool;
-            });
-            setUp();
-
-            // Pool should be empty at the beginning
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 0);
-            SUT.removedUnusedPools();
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 0);
-            PlcConnection connection = SUT.getConnection("dummydummy:single/socket1/socket2?fancyOption=true");
-            connection.close();
-
-            // after a connection we should have one pool
-            GenericObjectPool<PlcConnection> pool = atomicReference.get();
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 1);
-            SUT.removedUnusedPools();
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 1);
-
-            // now we force a eviction with your dummy policy
-            pool.evict();
-            SUT.removedUnusedPools();
-            assertThat(SUT.getStatistics()).containsEntry("pools.count", 0);
-            // and have no more open pools
-        }
-    }
-
     class DummyPlcConnection implements PlcConnection, PlcConnectionMetadata {
 
         private final String url;