You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by jf...@apache.org on 2021/01/03 15:21:11 UTC

[plc4x] 01/02: Renamed to pool2 and readded "old" connection-pool.

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

jfeinauer pushed a commit to branch feature/integrate-pool2
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit f4224876f2be47e33765148b590f1314f1b346b0
Author: Julian Feinauer <j....@pragmaticminds.de>
AuthorDate: Sun Jan 3 14:58:06 2021 +0100

    Renamed to pool2 and readded "old" connection-pool.
---
 plc4j/tools/connection-pool/pom.xml                |  56 +---
 .../connectionpool/CachedDriverManagerMBean.java   |  19 --
 .../plc4x/java/utils/connectionpool/PoolKey.java   |  73 ++++
 .../java/utils/connectionpool/PoolKeyFactory.java  | 110 ++++++
 .../connectionpool/PooledPlcConnectionFactory.java |  46 +++
 .../connectionpool/PooledPlcDriverManager.java     | 179 ++++++++++
 .../utils/connectionpool/PoolKeyFactoryTest.java   |  97 ++++++
 .../utils/connectionpool/PooledDummyDriver.java    |  52 +++
 .../connectionpool/PooledPlcDriverManagerTest.java | 367 +++++++++++++++++++++
 .../services/org.apache.plc4x.java.api.PlcDriver   |  19 ++
 .../connection-pool/src/test/resources/log4j2.xml  |  15 -
 .../connection-pool/src/test/resources/logback.xml |  34 ++
 .../{connection-pool => connection-pool2}/pom.xml  |   0
 .../connectionpool2}/CachedDriverManager.java      |   2 +-
 .../connectionpool2/CachedDriverManagerMBean.java} |  19 +-
 .../connectionpool2}/CachedPlcConnection.java      |   2 +-
 .../utils/connectionpool2}/CachedReadRequest.java  |   2 +-
 .../connectionpool2}/CachedReadRequestBuilder.java |   2 +-
 .../connectionpool2}/PlcConnectionFactory.java     |   2 +-
 .../connectionpool2}/PooledDriverManager.java      |   2 +-
 .../connectionpool2}/PooledDriverManagerMBean.java |   2 +-
 .../connectionpool2}/CachedDriverManagerIT.java    |   2 +-
 .../connectionpool2}/CachedDriverManagerMT.java    |   2 +-
 .../connectionpool2}/CachedDriverManagerTest.java  |   2 +-
 .../connectionpool2}/CachedPlcConnectionTest.java  |   4 +-
 .../connectionpool2}/PooledDriverManagerTest.java  |   4 +-
 .../connection-pool2/src/test/resources/log4j2.xml |  34 ++
 plc4j/tools/pom.xml                                |   1 +
 28 files changed, 1044 insertions(+), 105 deletions(-)

diff --git a/plc4j/tools/connection-pool/pom.xml b/plc4j/tools/connection-pool/pom.xml
index 84051c2..66c8463 100644
--- a/plc4j/tools/connection-pool/pom.xml
+++ b/plc4j/tools/connection-pool/pom.xml
@@ -37,63 +37,15 @@
       <groupId>org.apache.plc4x</groupId>
       <artifactId>plc4j-api</artifactId>
       <version>0.8.0-SNAPSHOT</version>
-      <scope>compile</scope>
     </dependency>
+
     <dependency>
       <groupId>org.apache.commons</groupId>
-      <artifactId>commons-lang3</artifactId>
-      <scope>compile</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-slf4j-impl</artifactId>
-      <version>2.11.1</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core</artifactId>
-      <version>2.11.1</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.plc4x</groupId>
-      <artifactId>plc4j-driver-mock</artifactId>
-      <version>0.8.0-SNAPSHOT</version>
-      <scope>test</scope>
-      <exclusions>
-        <exclusion>
-          <artifactId>junit</artifactId>
-          <groupId>junit</groupId>
-        </exclusion>
-      </exclusions>
+      <artifactId>commons-pool2</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.assertj</groupId>
-      <artifactId>assertj-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.awaitility</groupId>
-      <artifactId>awaitility</artifactId>
-      <version>3.0.0</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.awaitility</groupId>
-      <artifactId>awaitility-proxy</artifactId>
-      <version>3.0.0</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.junit.jupiter</groupId>
-      <artifactId>junit-jupiter</artifactId>
-      <scope>test</scope>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
     </dependency>
   </dependencies>
 
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerMBean.java b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerMBean.java
deleted file mode 100644
index 121c7ff..0000000
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerMBean.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.apache.plc4x.java.utils.connectionpool;
-
-public interface CachedDriverManagerMBean {
-
-    String getStateString();
-
-    int getNumberOfConnects();
-
-    int getNumberOfBorrows();
-
-    int getNumberOfWachtdogs();
-
-    int getNumberOfRejections();
-
-    void triggerReconnect();
-
-    int getQueueSize();
-
-}
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKey.java b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKey.java
new file mode 100644
index 0000000..7cd719a
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKey.java
@@ -0,0 +1,73 @@
+/*
+ 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 abstract class PoolKey {
+    protected final String url;
+    protected final PlcAuthentication plcAuthentication;
+
+    public PoolKey(String url, PlcAuthentication plcAuthentication) {
+        this.url = url;
+        this.plcAuthentication = plcAuthentication;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public PlcAuthentication getPlcAuthentication() {
+        return plcAuthentication;
+    }
+
+    /**
+     * @return the part of the url that should be pooled.
+     */
+    public abstract String getPoolableKey();
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof PoolKey)) {
+            return false;
+        }
+        PoolKey poolKey = (PoolKey) o;
+        return Objects.equals(getPoolableKey(), poolKey.getPoolableKey()) &&
+            Objects.equals(plcAuthentication, poolKey.plcAuthentication);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getPoolableKey(), plcAuthentication);
+    }
+
+    @Override
+    public String toString() {
+        return "PoolKey{" +
+            "url='" + url + '\'' +
+            (plcAuthentication != PooledPlcDriverManager.noPlcAuthentication ? ", plcAuthentication=" + plcAuthentication : "") +
+            '}';
+    }
+}
\ No newline at end of file
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactory.java b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactory.java
new file mode 100644
index 0000000..4341312
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactory.java
@@ -0,0 +1,110 @@
+/*
+ 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 org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class PoolKeyFactory {
+
+    // TODO 11.12.18 jf: add a property to the drivers to keep this generic
+    public PoolKey getPoolKey(String url, PlcAuthentication plcAuthentication) throws PlcConnectionException {
+        Objects.requireNonNull(url);
+        URI connectionUri;
+        try {
+            connectionUri = new URI(url);
+        } catch (URISyntaxException e) {
+            throw new PlcConnectionException("Invalid plc4j connection string '" + url + "'", e);
+        }
+        String protocol = connectionUri.getScheme().toLowerCase();
+        switch (protocol) {
+            // Currently this is disabled due to 2 reasons
+            // First, see PLC4X-223 it needs to be migrated to new URI Syntax
+            // Second, we have to decide which parameters uniquely identify a connection and which
+            // not. See PLC4X-224
+            /*
+            case "s7":
+                return new PoolKey(url, plcAuthentication) {
+                    private final Pattern s7URIPattern = Pattern.compile("^(?<poolablePart>s7://((?<ip>[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})|(?<hostname>[a-zA-Z0-9\\.\\-]+))(:(?<port>[0-9]{1,5}))?)(?<params>\\?.*)?");
+
+                    @Override
+                    public String getPoolableKey() {
+                        Matcher matcher = s7URIPattern.matcher(url);
+                        if (!matcher.matches()) {
+                            throw new IllegalArgumentException(url + " doesn't match " + s7URIPattern);
+                        }
+                        return Objects.requireNonNull(matcher.group("poolablePart"));
+                    }
+                };
+            case "ads":
+                return new PoolKey(url, plcAuthentication) {
+                    private final Pattern amsPortPattern = Pattern.compile("\\d+");
+                    private final Pattern amsNetIdPattern = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
+                    private final Pattern adsAddressPattern =
+                        Pattern.compile("(?<targetAmsNetId>" + amsNetIdPattern + "):(?<targetAmsPort>" + amsPortPattern + ")"
+                            + "(/"
+                            + "(?<sourceAmsNetId>" + amsNetIdPattern + "):(?<sourceAmsPort>" + amsPortPattern + ")"
+                            + ")?");
+                    private final Pattern inetAddressPattern = Pattern.compile("tcp://(?<host>[\\w.]+)(:(?<port>\\d*))?");
+                    private final Pattern serialPattern = Pattern.compile("serial://(?<serialDefinition>((?!/\\d).)*)");
+                    private final Pattern adsUriPattern = Pattern.compile("^(?<poolablePart>ads:(" + inetAddressPattern + "|" + serialPattern + "))/" + adsAddressPattern + "(\\?.*)?");
+
+                    @Override
+                    public String getPoolableKey() {
+                        Matcher matcher =
+                            adsUriPattern.matcher(url);
+                        if (!matcher.matches()) {
+                            throw new IllegalArgumentException(url + " doesn't match " + adsUriPattern);
+                        }
+                        return Objects.requireNonNull(matcher.group("poolablePart"));
+                    }
+                };
+            case "modbus":
+                return new PoolKey(url, plcAuthentication) {
+                    private final Pattern inetAddressPattern = Pattern.compile("tcp://(?<host>[\\w.]+)(:(?<port>\\d*))?");
+                    private final Pattern serialPattern = Pattern.compile("serial://(?<serialDefinition>((?!/\\d).)*)");
+                    private final Pattern modbusUriPattern = Pattern.compile("^(?<poolablePart>modbus:(" + inetAddressPattern + "|" + serialPattern + "))/?" + "(?<params>\\?.*)?");
+
+                    @Override
+                    public String getPoolableKey() {
+                        Matcher matcher = modbusUriPattern.matcher(url);
+                        if (!matcher.matches()) {
+                            throw new IllegalArgumentException(url + " doesn't match " + modbusUriPattern);
+                        }
+                        return Objects.requireNonNull(matcher.group("poolablePart"));
+                    }
+                };
+             */
+            default:
+                return new PoolKey(url, plcAuthentication) {
+                    @Override
+                    public String getPoolableKey() {
+                        return url;
+                    }
+                };
+        }
+    }
+}
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcConnectionFactory.java b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcConnectionFactory.java
new file mode 100644
index 0000000..03879eb
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcConnectionFactory.java
@@ -0,0 +1,46 @@
+/*
+ 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.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 BaseKeyedPooledObjectFactory<PoolKey, PlcConnection> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(PooledPlcConnectionFactory.class);
+
+    @Override
+    public PooledObject<PlcConnection> wrap(PlcConnection plcConnection) {
+        LOGGER.debug("Wrapping connection {}", plcConnection);
+        return new DefaultPooledObject<>(plcConnection);
+    }
+
+    @Override
+    public void destroyObject(PoolKey key, PooledObject<PlcConnection> p) throws Exception {
+        p.getObject().close();
+    }
+
+    @Override
+    public boolean validateObject(PoolKey key, PooledObject<PlcConnection> p) {
+        return p.getObject().isConnected();
+    }
+}
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManager.java b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManager.java
new file mode 100644
index 0000000..c164491
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManager.java
@@ -0,0 +1,179 @@
+/*
+ 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.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.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class PooledPlcDriverManager extends PlcDriverManager {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(PooledPlcDriverManager.class);
+
+    private KeyedObjectPool<PoolKey, PlcConnection> keyedObjectPool;
+
+    // Marker class do detected a non null value
+    static final NoPlcAuthentication noPlcAuthentication = new NoPlcAuthentication();
+
+    private final PoolKeyFactory poolKeyFactory;
+
+    public PooledPlcDriverManager() {
+        this(GenericKeyedObjectPool::new);
+    }
+
+    public PooledPlcDriverManager(PoolKeyFactory poolKeyFactory) {
+        this(GenericKeyedObjectPool::new, poolKeyFactory);
+    }
+
+    public PooledPlcDriverManager(ClassLoader classLoader) {
+        this(classLoader, new PoolKeyFactory());
+    }
+
+    public PooledPlcDriverManager(ClassLoader classLoader, PoolKeyFactory poolKeyFactory) {
+        super(classLoader);
+        setFromPoolCreator(GenericKeyedObjectPool::new);
+        this.poolKeyFactory = poolKeyFactory;
+    }
+
+    public PooledPlcDriverManager(PoolCreator poolCreator) {
+        this(poolCreator, new PoolKeyFactory());
+    }
+
+    public PooledPlcDriverManager(PoolCreator poolCreator, PoolKeyFactory poolKeyFactory) {
+        setFromPoolCreator(poolCreator);
+        this.poolKeyFactory = poolKeyFactory;
+    }
+
+    public PooledPlcDriverManager(ClassLoader classLoader, PoolCreator poolCreator) {
+        super(classLoader);
+        setFromPoolCreator(poolCreator);
+        poolKeyFactory = new PoolKeyFactory();
+    }
+
+    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
+    public PlcConnection getConnection(String url) throws PlcConnectionException {
+        return getConnection(url, noPlcAuthentication);
+    }
+
+    @Override
+    public PlcConnection getConnection(String url, PlcAuthentication authentication) throws PlcConnectionException {
+        PoolKey poolKey = poolKeyFactory.getPoolKey(url, authentication);
+        if (LOGGER.isDebugEnabled()) {
+            if (authentication != noPlcAuthentication) {
+                LOGGER.debug("Try to borrow an object for url {} and authentication {}", url, authentication);
+            } else {
+                LOGGER.debug("Try to borrow an object for url {}", url);
+            }
+        }
+        PlcConnection plcConnection;
+        try {
+            plcConnection = keyedObjectPool.borrowObject(poolKey);
+            if (plcConnection.isConnected() == false) {
+                LOGGER.debug("Attempting to reconnect to device");
+                plcConnection.connect();
+            }
+        } catch (Exception e) {
+            throw new PlcConnectionException(e);
+        }
+        // Used to invalidate a proxy
+        AtomicBoolean proxyInvalidated = new AtomicBoolean(false);
+        return (PlcConnection) Proxy.newProxyInstance(classLoader, new Class[]{PlcConnection.class}, (proxy, method, args) -> {
+            if (proxyInvalidated.get()) {
+                throw new IllegalStateException("Proxy not valid anymore");
+            }
+            if ("close".equals(method.getName())) {
+                LOGGER.debug("close called on {}", plcConnection);
+                proxyInvalidated.set(true);
+                keyedObjectPool.returnObject(poolKey, plcConnection);
+                return null;
+            } else {
+                try {
+                    return method.invoke(plcConnection, args);
+                } catch (InvocationTargetException e) {
+                    if (e.getCause().getClass() == PlcConnectionException.class) {
+                        keyedObjectPool.invalidateObject(poolKey, plcConnection);
+                        proxyInvalidated.set(true);
+                    }
+                    throw e;
+                }
+            }
+        });
+    }
+
+    @FunctionalInterface
+    public interface PoolCreator {
+        KeyedObjectPool<PoolKey, PlcConnection> createPool(PooledPlcConnectionFactory pooledPlcConnectionFactory);
+    }
+
+    // TODO: maybe export to jmx // generic poolKey has builtin jmx too
+    public Map<String, Number> getStatistics() {
+        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;
+    }
+
+    private static final class NoPlcAuthentication implements PlcAuthentication {
+
+    }
+}
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactoryTest.java b/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactoryTest.java
new file mode 100644
index 0000000..7e345ef
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactoryTest.java
@@ -0,0 +1,97 @@
+/*
+ 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.assertj.core.api.WithAssertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+/**
+ * See Cahnges due to PLC4X-223 and PLC4X-224
+ */
+@Disabled
+class PoolKeyFactoryTest implements WithAssertions {
+
+    private PoolKeyFactory SUT = new PoolKeyFactory();
+
+    @Nested
+    class Generic {
+        @Test
+        void getPoolKey() throws Exception {
+            PoolKey poolKey = SUT.getPoolKey("randomProtocol://randomHost/1/1?someOptions", PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getUrl()).isEqualTo("randomProtocol://randomHost/1/1?someOptions");
+            assertThat(poolKey.getPlcAuthentication()).isEqualTo(PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getPoolableKey()).isEqualTo("randomProtocol://randomHost/1/1?someOptions");
+        }
+    }
+
+    @Nested
+    class S7 {
+        @Test
+        void getPoolKey() throws Exception {
+            PoolKey poolKey = SUT.getPoolKey("s7://localhost?randomOption=true", PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getUrl()).isEqualTo("s7://localhost?randomOption=true");
+            assertThat(poolKey.getPlcAuthentication()).isEqualTo(PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getPoolableKey()).isEqualTo("s7://localhost");
+        }
+    }
+
+    @Nested
+    class ADS {
+        @Test
+        void getPoolKey_TCP() throws Exception {
+            PoolKey poolKey = SUT.getPoolKey("ads:tcp://10.10.64.40/10.10.64.40.1.1:851/10.10.56.23.1.1:30000", PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getUrl()).isEqualTo("ads:tcp://10.10.64.40/10.10.64.40.1.1:851/10.10.56.23.1.1:30000");
+            assertThat(poolKey.getPlcAuthentication()).isEqualTo(PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getPoolableKey()).isEqualTo("ads:tcp://10.10.64.40");
+        }
+
+        @Test
+        void getPoolKey_SERIAL() throws Exception {
+            PoolKey poolKey = SUT.getPoolKey("ads:serial:///dev/ttys003/10.10.64.40.1.1:851/10.10.56.23.1.1:30000", PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getUrl()).isEqualTo("ads:serial:///dev/ttys003/10.10.64.40.1.1:851/10.10.56.23.1.1:30000");
+            assertThat(poolKey.getPlcAuthentication()).isEqualTo(PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getPoolableKey()).isEqualTo("ads:serial:///dev/ttys003");
+        }
+    }
+
+    @Nested
+    class Modbus {
+        @Test
+        void getPoolKey_TCP() throws Exception {
+            PoolKey poolKey = SUT.getPoolKey("modbus:tcp://10.10.64.40?someRandomOption=true", PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getUrl()).isEqualTo("modbus:tcp://10.10.64.40?someRandomOption=true");
+            assertThat(poolKey.getPlcAuthentication()).isEqualTo(PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getPoolableKey()).isEqualTo("modbus:tcp://10.10.64.40");
+        }
+
+        @Disabled("Modbus serial pooling doesn't work right now as intended")
+        @Test
+        void getPoolKey_SERIAL() throws Exception {
+            PoolKey poolKey = SUT.getPoolKey("modbus:serial:///dev/ttys003?someRandomOption=true", PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getUrl()).isEqualTo("modbus:serial:///dev/ttys003?someRandomOption=true");
+            assertThat(poolKey.getPlcAuthentication()).isEqualTo(PooledPlcDriverManager.noPlcAuthentication);
+            assertThat(poolKey.getPoolableKey()).isEqualTo("modbus:serial:///dev/ttys003");
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledDummyDriver.java b/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledDummyDriver.java
new file mode 100644
index 0000000..c572e8d
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledDummyDriver.java
@@ -0,0 +1,52 @@
+/*
+ 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.PlcConnection;
+import org.apache.plc4x.java.api.authentication.PlcAuthentication;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.apache.plc4x.java.api.PlcDriver;
+
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+
+public class PooledDummyDriver implements PlcDriver {
+
+    private PlcDriver mockedPlcDriver = mock(PlcDriver.class, RETURNS_DEEP_STUBS);
+
+    @Override
+    public String getProtocolCode() {
+        return PooledDummyDriver.class.getName();
+    }
+
+    @Override
+    public String getProtocolName() {
+        return mockedPlcDriver.getProtocolCode();
+    }
+
+    @Override
+    public PlcConnection getConnection(String url) throws PlcConnectionException {
+        return mockedPlcDriver.getConnection(url);
+    }
+
+    @Override
+    public PlcConnection getConnection(String url, PlcAuthentication authentication) throws PlcConnectionException {
+        return mockedPlcDriver.getConnection(url, authentication);
+    }
+
+}
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java b/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java
new file mode 100644
index 0000000..aedd2c4
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java
@@ -0,0 +1,367 @@
+/*
+ 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.commons.lang3.reflect.FieldUtils;
+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;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.apache.plc4x.java.api.exceptions.PlcUnsupportedOperationException;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest;
+import org.apache.plc4x.java.api.messages.PlcUnsubscriptionRequest;
+import org.apache.plc4x.java.api.messages.PlcWriteRequest;
+import org.apache.plc4x.java.api.metadata.PlcConnectionMetadata;
+import org.apache.plc4x.java.api.PlcDriver;
+import org.assertj.core.api.WithAssertions;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.IntStream;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class PooledPlcDriverManagerTest implements WithAssertions {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(PooledPlcDriverManagerTest.class);
+
+    private PooledPlcDriverManager SUT = new PooledPlcDriverManager(pooledPlcConnectionFactory -> {
+        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)
+    PlcDriver plcDriver;
+
+    private ExecutorService executorService;
+
+    @SuppressWarnings("unchecked")
+    @BeforeEach
+    void setUp() throws Exception {
+        Map<String, PlcDriver> driverMap = (Map) FieldUtils.getField(PooledPlcDriverManager.class, "driverMap", true).get(SUT);
+        driverMap.put("dummydummy", plcDriver);
+        executorService = Executors.newFixedThreadPool(100);
+
+        assertThat(SUT.getStatistics()).containsOnly(
+            entry("pools.count", 0),
+            entry("numActive", 0),
+            entry("numIdle", 0)
+        );
+    }
+
+    @AfterEach
+    void tearDown() {
+        executorService.shutdown();
+    }
+
+    @Test
+    void getConnection() throws Exception {
+        when(plcDriver.getConnection(anyString())).then(invocationOnMock -> new DummyPlcConnection(invocationOnMock.getArgument(0)));
+
+        LinkedList<Callable<PlcConnection>> callables = new LinkedList<>();
+
+        // This: should result in one open connection
+        IntStream.range(0, 8).forEach(i -> callables.add(() -> {
+            try {
+                return SUT.getConnection("dummydummy:single/socket1/socket2?fancyOption=true");
+            } catch (PlcConnectionException e) {
+                throw new RuntimeException(e);
+            }
+        }));
+
+        // This should result in five open connections
+        IntStream.range(0, 5).forEach(i -> callables.add(() -> {
+            try {
+                return SUT.getConnection("dummydummy:multi-" + i + "/socket1/socket2?fancyOption=true");
+            } catch (PlcConnectionException e) {
+                throw new RuntimeException(e);
+            }
+        }));
+
+        List<Future<PlcConnection>> futures = executorService.invokeAll(callables);
+
+        // Wait for existing connections
+        futures.forEach(plcConnectionFuture1 -> {
+            try {
+                plcConnectionFuture1.get();
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
+        });
+        LOGGER.info("Statistics after execution {}", SUT.getStatistics());
+
+        // As we have a pool size of 8 we should have only 8 + 5 calls for the separate pools
+        verify(plcDriver, times(13)).getConnection(anyString());
+
+        assertThat(SUT.getStatistics()).contains(
+            entry("PoolKey{url='dummydummy:single/socket1/socket2?fancyOption=true'}.numActive", 8)
+        );
+
+        futures.forEach(plcConnectionFuture -> {
+            try {
+                plcConnectionFuture.get().close();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        assertThat(SUT.getStatistics()).contains(
+            entry("PoolKey{url='dummydummy:single/socket1/socket2?fancyOption=true'}.numActive", 0)
+        );
+    }
+
+    @Test
+    void getConnectionWithAuth() throws Exception {
+        when(plcDriver.getConnection(anyString(), any())).then(invocationOnMock -> new DummyPlcConnection(invocationOnMock.getArgument(0), invocationOnMock.getArgument(1)));
+
+        LinkedList<Callable<PlcConnection>> callables = new LinkedList<>();
+
+        // This: should result in one open connection
+        IntStream.range(0, 8).forEach(i -> callables.add(() -> {
+            try {
+                return SUT.getConnection("dummydummy:single/socket1/socket2?fancyOption=true", new PlcUsernamePasswordAuthentication("user", "passwordp954368564098ß"));
+            } catch (PlcConnectionException e) {
+                throw new RuntimeException(e);
+            }
+        }));
+
+        // This should result in five open connections
+        IntStream.range(0, 5).forEach(i -> callables.add(() -> {
+            try {
+                return SUT.getConnection("dummydummy:multi-" + i + "/socket1/socket2?fancyOption=true", new PlcUsernamePasswordAuthentication("user", "passwordp954368564098ß"));
+            } catch (PlcConnectionException e) {
+                throw new RuntimeException(e);
+            }
+        }));
+
+        List<Future<PlcConnection>> futures = executorService.invokeAll(callables);
+
+        futures.forEach(plcConnectionFuture1 -> {
+            try {
+                plcConnectionFuture1.get();
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        LOGGER.info("Statistics after execution {}", SUT.getStatistics());
+
+        // As we have a pool size of 8 we should have only 8 + 5 calls for the separate pools
+        verify(plcDriver, times(13)).getConnection(anyString(), any());
+
+        assertThat(SUT.getStatistics()).contains(
+            entry("PoolKey{url='dummydummy:single/socket1/socket2?fancyOption=true', plcAuthentication=PlcUsernamePasswordAuthentication{username='user', password='*****************'}}.numActive", 8)
+        );
+
+        futures.forEach(plcConnectionFuture -> {
+            try {
+                plcConnectionFuture.get().close();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        assertThat(SUT.getStatistics()).contains(
+            entry("PoolKey{url='dummydummy:single/socket1/socket2?fancyOption=true', plcAuthentication=PlcUsernamePasswordAuthentication{username='user', password='*****************'}}.numActive", 0)
+        );
+    }
+
+    @Test
+    void connectionInvalidation() throws Exception {
+        when(plcDriver.getConnection(anyString())).then(invocationOnMock -> new DummyPlcConnection(invocationOnMock.getArgument(0)));
+
+        PlcConnection connection = SUT.getConnection("dummydummy:single/socket1/socket2?fancyOption=true");
+        assertThat(connection.isConnected()).isEqualTo(true);
+        assertThat(connection.getMetadata().canRead()).isEqualTo(false);
+        assertThat(connection.getMetadata().canWrite()).isEqualTo(false);
+        assertThat(connection.getMetadata().canSubscribe()).isEqualTo(false);
+
+        connection.close();
+        assertThatThrownBy(connection::connect).isInstanceOf(IllegalStateException.class).hasMessage("Proxy not valid anymore");
+        assertThatThrownBy(connection::isConnected).isInstanceOf(IllegalStateException.class).hasMessage("Proxy not valid anymore");
+        assertThatThrownBy(connection::close).isInstanceOf(IllegalStateException.class).hasMessage("Proxy not valid anymore");
+        assertThatThrownBy(connection::getMetadata).isInstanceOf(IllegalStateException.class).hasMessage("Proxy not valid anymore");
+        assertThatThrownBy(connection::readRequestBuilder).isInstanceOf(IllegalStateException.class).hasMessage("Proxy not valid anymore");
+        assertThatThrownBy(connection::writeRequestBuilder).isInstanceOf(IllegalStateException.class).hasMessage("Proxy not valid anymore");
+        assertThatThrownBy(connection::subscriptionRequestBuilder).isInstanceOf(IllegalStateException.class).hasMessage("Proxy not valid anymore");
+        assertThatThrownBy(connection::unsubscriptionRequestBuilder).isInstanceOf(IllegalStateException.class).hasMessage("Proxy not valid anymore");
+    }
+
+    @Test
+    void cleanupOfBrokenConnections() throws Exception {
+        AtomicBoolean failNow = new AtomicBoolean(false);
+        when(plcDriver.getConnection(anyString())).then(invocationOnMock -> {
+            DummyPlcConnection dummyPlcConnection = spy(new DummyPlcConnection(invocationOnMock.getArgument(0)));
+            // we fake an connection which breaks at this call
+            doAnswer(invocation -> {
+                if (failNow.get()) {
+                    throw new PlcConnectionException("blub");
+                }
+                return invocation.callRealMethod();
+            }).when(dummyPlcConnection).connect();
+            return dummyPlcConnection;
+        });
+
+        assertThat(SUT.getStatistics()).containsOnly(
+            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("numActive", 1),
+            entry("numIdle", 0),
+            entry("PoolKey{url='dummydummy:breakIt'}.numActive", 1)
+        );
+        failNow.set(true);
+        try {
+            connection.connect();
+            fail("This should throw an exception");
+        } catch (Exception e) {
+            // TODO: currently UndeclaredThrowableException is the top one which should be InvocationTargetException
+            //assertThat(e).isInstanceOf(InvocationTargetException.class);
+            assertThat(e).hasRootCauseInstanceOf(PlcConnectionException.class);
+        }
+        // Faulty connection should have been discarded
+        assertThat(SUT.getStatistics()).containsOnly(
+            entry("pools.count", 0),
+            entry("numActive", 0),
+            entry("numIdle", 0)
+        );
+    }
+
+    @Test
+    public void testOtherConstructors() {
+        assertThat(new PooledPlcDriverManager()).isNotNull();
+        assertThat(new PooledPlcDriverManager(new PoolKeyFactory())).isNotNull();
+        assertThat(new PooledPlcDriverManager(PooledPlcDriverManager.class.getClassLoader())).isNotNull();
+        assertThat(new PooledPlcDriverManager(
+            PooledPlcDriverManager.class.getClassLoader(), new PoolKeyFactory())).isNotNull();
+    }
+
+    class DummyPlcConnection implements PlcConnection, PlcConnectionMetadata {
+
+        private final String url;
+
+        private final PlcAuthentication plcAuthentication;
+
+        boolean connected = false;
+
+        public DummyPlcConnection(String url) {
+            this(url, null);
+        }
+
+        public DummyPlcConnection(String url, PlcAuthentication plcAuthentication) {
+            this.url = url;
+            this.plcAuthentication = plcAuthentication;
+        }
+
+        @Override
+        public void connect() {
+            connected = true;
+        }
+
+        @Override
+        public CompletableFuture<Void> ping() {
+            CompletableFuture<Void> future = new CompletableFuture<>();
+            future.completeExceptionally(new UnsupportedOperationException());
+            return future;
+        }
+
+        @Override
+        public boolean isConnected() {
+            return connected;
+        }
+
+        @Override
+        public PlcConnectionMetadata getMetadata() {
+            return this;
+        }
+
+        @Override
+        public boolean canRead() {
+            return false;
+        }
+
+        @Override
+        public boolean canWrite() {
+            return false;
+        }
+
+        @Override
+        public boolean canSubscribe() {
+            return false;
+        }
+
+        @Override
+        public void close() {
+            connected = false;
+        }
+
+        @Override
+        public PlcReadRequest.Builder readRequestBuilder() {
+            throw new PlcUnsupportedOperationException("The connection does not support reading");
+        }
+
+        @Override
+        public PlcWriteRequest.Builder writeRequestBuilder() {
+            throw new PlcUnsupportedOperationException("The connection does not support writing");
+        }
+
+        @Override
+        public PlcSubscriptionRequest.Builder subscriptionRequestBuilder() {
+            throw new PlcUnsupportedOperationException("The connection does not support subscription");
+        }
+
+        @Override
+        public PlcUnsubscriptionRequest.Builder unsubscriptionRequestBuilder() {
+            throw new PlcUnsupportedOperationException("The connection does not support subscription");
+        }
+
+        @Override
+        public String toString() {
+            return "DummyPlcConnection{" +
+                "url='" + url + '\'' +
+                ", plcAuthentication=" + plcAuthentication +
+                ", connected=" + connected +
+                '}';
+        }
+    }
+}
\ No newline at end of file
diff --git a/plc4j/tools/connection-pool/src/test/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver b/plc4j/tools/connection-pool/src/test/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
new file mode 100644
index 0000000..a2d97ed
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/test/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+org.apache.plc4x.java.utils.connectionpool.PooledDummyDriver
diff --git a/plc4j/tools/connection-pool/src/test/resources/log4j2.xml b/plc4j/tools/connection-pool/src/test/resources/log4j2.xml
deleted file mode 100644
index e8efef9..0000000
--- a/plc4j/tools/connection-pool/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="WARN">
-    <Appenders>
-        <Console name="Console" target="SYSTEM_OUT">
-            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
-        </Console>
-    </Appenders>
-    <Loggers>
-        <Root level="WARN">
-            <AppenderRef ref="Console"/>
-        </Root>
-        <Logger name="org.pragmaticindustries.cockpit.plc" level="DEBUG"/>
-        <Logger name="org.pragmaticindustries.cockpit.plc.pool2" level="DEBUG"/>
-    </Loggers>
-</Configuration>
\ No newline at end of file
diff --git a/plc4j/tools/connection-pool/src/test/resources/logback.xml b/plc4j/tools/connection-pool/src/test/resources/logback.xml
new file mode 100644
index 0000000..31c49f0
--- /dev/null
+++ b/plc4j/tools/connection-pool/src/test/resources/logback.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
+               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+               xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <!-- encoders are assigned the type
+         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="DEBUG">
+    <appender-ref ref="STDOUT" />
+  </root>
+
+</configuration>
\ No newline at end of file
diff --git a/plc4j/tools/connection-pool/pom.xml b/plc4j/tools/connection-pool2/pom.xml
similarity index 100%
copy from plc4j/tools/connection-pool/pom.xml
copy to plc4j/tools/connection-pool2/pom.xml
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManager.java b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManager.java
similarity index 99%
rename from plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManager.java
rename to plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManager.java
index 89e4428..06a881d 100644
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManager.java
+++ b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManager.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.commons.lang3.NotImplementedException;
 import org.apache.plc4x.java.PlcDriverManager;
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerMBean.java b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerMBean.java
similarity index 72%
copy from plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerMBean.java
copy to plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerMBean.java
index 9b63ec2..7b26538 100644
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerMBean.java
+++ b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerMBean.java
@@ -17,9 +17,22 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
-public interface PooledDriverManagerMBean {
+public interface CachedDriverManagerMBean {
+
+    String getStateString();
+
+    int getNumberOfConnects();
+
+    int getNumberOfBorrows();
+
+    int getNumberOfWachtdogs();
+
+    int getNumberOfRejections();
+
+    void triggerReconnect();
+
+    int getQueueSize();
 
-    String[] getConnectedUrls();
 }
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedPlcConnection.java b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnection.java
similarity index 99%
rename from plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedPlcConnection.java
rename to plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnection.java
index 4e7a1af..d97fc93 100644
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedPlcConnection.java
+++ b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnection.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedReadRequest.java b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedReadRequest.java
similarity index 97%
rename from plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedReadRequest.java
rename to plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedReadRequest.java
index a7b0213..51a3a0a 100644
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedReadRequest.java
+++ b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedReadRequest.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.api.messages.PlcReadRequest;
 import org.apache.plc4x.java.api.messages.PlcReadResponse;
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedReadRequestBuilder.java b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedReadRequestBuilder.java
similarity index 97%
rename from plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedReadRequestBuilder.java
rename to plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedReadRequestBuilder.java
index eda1f32..742e5b3 100644
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/CachedReadRequestBuilder.java
+++ b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedReadRequestBuilder.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.api.messages.PlcReadRequest;
 import org.apache.plc4x.java.api.model.PlcField;
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PlcConnectionFactory.java b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PlcConnectionFactory.java
similarity index 95%
rename from plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PlcConnectionFactory.java
rename to plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PlcConnectionFactory.java
index 4d72c04..b5b58da 100644
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PlcConnectionFactory.java
+++ b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PlcConnectionFactory.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManager.java b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManager.java
similarity index 98%
rename from plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManager.java
rename to plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManager.java
index 1056c00..fc36879 100644
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManager.java
+++ b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManager.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.PlcDriverManager;
 import org.apache.plc4x.java.api.PlcConnection;
diff --git a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerMBean.java b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManagerMBean.java
similarity index 94%
rename from plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerMBean.java
rename to plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManagerMBean.java
index 9b63ec2..8555e6b 100644
--- a/plc4j/tools/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerMBean.java
+++ b/plc4j/tools/connection-pool2/src/main/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManagerMBean.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 public interface PooledDriverManagerMBean {
 
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerIT.java b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerIT.java
similarity index 97%
rename from plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerIT.java
rename to plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerIT.java
index 9e618b4..086863b 100644
--- a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerIT.java
+++ b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerIT.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.mock.connection.MockConnection;
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerMT.java b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerMT.java
similarity index 98%
rename from plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerMT.java
rename to plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerMT.java
index 7727a98..cb69a2f 100644
--- a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerMT.java
+++ b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerMT.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.PlcDriverManager;
 import org.apache.plc4x.java.api.PlcConnection;
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerTest.java b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerTest.java
similarity index 99%
rename from plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerTest.java
rename to plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerTest.java
index 47abf78..f3f130b 100644
--- a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedDriverManagerTest.java
+++ b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedDriverManagerTest.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedPlcConnectionTest.java b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnectionTest.java
similarity index 95%
rename from plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedPlcConnectionTest.java
rename to plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnectionTest.java
index 096b058..1df6766 100644
--- a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/CachedPlcConnectionTest.java
+++ b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnectionTest.java
@@ -17,12 +17,10 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.messages.PlcReadResponse;
-import org.apache.plc4x.java.utils.connectionpool.CachedDriverManager;
-import org.apache.plc4x.java.utils.connectionpool.CachedPlcConnection;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerTest.java b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManagerTest.java
similarity index 90%
rename from plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerTest.java
rename to plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManagerTest.java
index b1285df..06b341c 100644
--- a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledDriverManagerTest.java
+++ b/plc4j/tools/connection-pool2/src/test/java/org/apache/plc4x/java/utils/connectionpool2/PooledDriverManagerTest.java
@@ -17,12 +17,10 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.utils.connectionpool;
+package org.apache.plc4x.java.utils.connectionpool2;
 
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
-import org.apache.plc4x.java.utils.connectionpool.CachedDriverManager;
-import org.apache.plc4x.java.utils.connectionpool.PooledDriverManager;
 import org.assertj.core.api.WithAssertions;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
diff --git a/plc4j/tools/connection-pool2/src/test/resources/log4j2.xml b/plc4j/tools/connection-pool2/src/test/resources/log4j2.xml
new file mode 100644
index 0000000..2db650b
--- /dev/null
+++ b/plc4j/tools/connection-pool2/src/test/resources/log4j2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<Configuration status="WARN">
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
+        </Console>
+    </Appenders>
+    <Loggers>
+        <Root level="WARN">
+            <AppenderRef ref="Console"/>
+        </Root>
+        <Logger name="org.pragmaticindustries.cockpit.plc" level="DEBUG"/>
+        <Logger name="org.pragmaticindustries.cockpit.plc.pool2" level="DEBUG"/>
+    </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/plc4j/tools/pom.xml b/plc4j/tools/pom.xml
index aaba917..af74031 100644
--- a/plc4j/tools/pom.xml
+++ b/plc4j/tools/pom.xml
@@ -36,6 +36,7 @@
   <modules>
     <module>capture-replay</module>
     <module>connection-pool</module>
+    <module>connection-pool2</module>
     <module>opm</module>
     <module>scraper</module>
   </modules>