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
commit fa2688a617713e62a194be45d0ae1acecac628de Author: Sebastian Rühl <sru...@apache.org> AuthorDate: Thu Nov 1 17:49:32 2018 +0100 [plc4j-pool] added PoolKeyFactory to produce different keys for different protocols. --- .../plc4x/java/utils/connectionpool/PoolKey.java | 21 ++--- .../java/utils/connectionpool/PoolKeyFactory.java | 103 +++++++++++++++++++++ .../connectionpool/PooledPlcDriverManager.java | 19 +++- .../utils/connectionpool/PoolKeyFactoryTest.java | 93 +++++++++++++++++++ 4 files changed, 224 insertions(+), 12 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 index 166589c..7cd719a 100644 --- 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 @@ -23,21 +23,15 @@ import org.apache.plc4x.java.api.authentication.PlcAuthentication; import java.util.Objects; -public class PoolKey { - final String url; - final PlcAuthentication plcAuthentication; +public abstract class PoolKey { + protected final String url; + protected 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; } @@ -46,6 +40,11 @@ public class PoolKey { 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) { @@ -55,13 +54,13 @@ public class PoolKey { return false; } PoolKey poolKey = (PoolKey) o; - return Objects.equals(url, poolKey.url) && + return Objects.equals(getPoolableKey(), poolKey.getPoolableKey()) && Objects.equals(plcAuthentication, poolKey.plcAuthentication); } @Override public int hashCode() { - return Objects.hash(url, plcAuthentication); + return Objects.hash(getPoolableKey(), plcAuthentication); } @Override diff --git a/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactory.java b/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactory.java new file mode 100644 index 0000000..e5d73c1 --- /dev/null +++ b/plc4j/utils/connection-pool/src/main/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactory.java @@ -0,0 +1,103 @@ +/* + 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 { + + 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) { + case "s7": + return new PoolKey(url, plcAuthentication) { + private final Pattern s7URIPattern = Pattern.compile("^(?<poolablePart>s7://(?<host>.*)/(?<rack>\\d{1,4})/(?<slot>\\d{1,4}))(?<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/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 d10dc20..9a7e5d7 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 @@ -43,22 +43,39 @@ public class PooledPlcDriverManager extends PlcDriverManager { // 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) { @@ -85,7 +102,7 @@ public class PooledPlcDriverManager extends PlcDriverManager { @Override public PlcConnection getConnection(String url, PlcAuthentication authentication) throws PlcConnectionException { - PoolKey poolKey = PoolKey.of(url, authentication); + 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); diff --git a/plc4j/utils/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactoryTest.java b/plc4j/utils/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactoryTest.java new file mode 100644 index 0000000..a73f9c7 --- /dev/null +++ b/plc4j/utils/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PoolKeyFactoryTest.java @@ -0,0 +1,93 @@ +/* + 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; + +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/1/2?randomOption=true", PooledPlcDriverManager.noPlcAuthentication); + assertThat(poolKey.getUrl()).isEqualTo("s7://localhost/1/2?randomOption=true"); + assertThat(poolKey.getPlcAuthentication()).isEqualTo(PooledPlcDriverManager.noPlcAuthentication); + assertThat(poolKey.getPoolableKey()).isEqualTo("s7://localhost/1/2"); + } + } + + @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