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

chrisdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 808a663bbf3f1a1bc73a196d78ac1d121d76ef91
Author: Christofer Dutz <[email protected]>
AuthorDate: Mon Jun 22 11:51:20 2026 +0200

    feat: Added a check to the drivers that validate if a selected transport is 
supported and if not to throw an exception (However adding a 
configuration-parameter to forcefully disable this check)
---
 .../plc4x/java/s7/discovery/S7PlcDiscoverer.java   |  2 +-
 .../apache/plc4x/java/spi/drivers/DriverBase.java  | 25 ++++++++-
 .../config/ConnectionControlConfiguration.java     | 65 ++++++++++++++++++++++
 .../driver/internal/ConnectionManager.java         |  8 +++
 4 files changed, 98 insertions(+), 2 deletions(-)

diff --git 
a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/discovery/S7PlcDiscoverer.java
 
b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/discovery/S7PlcDiscoverer.java
index 81155f5da6..ebe8b3417f 100644
--- 
a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/discovery/S7PlcDiscoverer.java
+++ 
b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/discovery/S7PlcDiscoverer.java
@@ -248,7 +248,7 @@ public class S7PlcDiscoverer implements PlcDiscoverer {
 
         String displayName = deviceTypeName + " - " + deviceName;
         PlcDiscoveryItem item = new DefaultPlcDiscoveryItem(
-            "s7", "tcp", ip.address, options, displayName, attributes);
+            "s7", "cotp", ip.address, options, displayName, attributes);
         values.add(item);
         if (handler != null) {
             handler.handle(item);
diff --git 
a/plc4j/spi/drivers/src/main/java/org/apache/plc4x/java/spi/drivers/DriverBase.java
 
b/plc4j/spi/drivers/src/main/java/org/apache/plc4x/java/spi/drivers/DriverBase.java
index dd45bc1019..7a172dadc9 100644
--- 
a/plc4j/spi/drivers/src/main/java/org/apache/plc4x/java/spi/drivers/DriverBase.java
+++ 
b/plc4j/spi/drivers/src/main/java/org/apache/plc4x/java/spi/drivers/DriverBase.java
@@ -40,6 +40,7 @@ import 
org.apache.plc4x.java.spi.config.annotations.defaults.IntDefaultValue;
 import org.apache.plc4x.java.spi.config.annotations.defaults.LongDefaultValue;
 import org.apache.plc4x.java.spi.config.annotations.defaults.ShortDefaultValue;
 import 
org.apache.plc4x.java.spi.config.annotations.defaults.StringDefaultValue;
+import org.apache.plc4x.java.spi.drivers.config.ConnectionControlConfiguration;
 import org.apache.plc4x.java.spi.drivers.functions.PlcDiscoverer;
 import org.apache.plc4x.java.spi.drivers.messages.DefaultPlcDiscoveryRequest;
 import org.apache.plc4x.java.spi.drivers.messages.metadata.DefaultOption;
@@ -169,6 +170,29 @@ public abstract class DriverBase implements PlcDriver {
             throw new PlcConnectionException(
                 "This driver is not suited to handle this connection string");
         }
+        ConfigurationFactory configurationFactory = new ConfigurationFactory();
+
+        // Enforce that the selected transport is one this driver actually 
supports.
+        // Drivers declare their supported transports via 
getSupportedTransportCodes(); the metadata
+        // getter falls back to the single default transport when no explicit 
list is declared, so a
+        // driver that only declares a default still yields a non-empty 
supported set here. Pairing a
+        // driver with a transport it does not support - e.g. a 'tcp' 
transport with the S7 driver,
+        // which speaks COTP - used to be silently accepted and then 
misbehave; we now fail fast with a
+        // clear, actionable message. The 'allow-unsupported-transport' 
connection option intentionally
+        // bypasses ONLY this driver-specific check; it does NOT bypass the 
'is the transport registered
+        // at all' lookup further below.
+        ConnectionControlConfiguration connectionControlConfiguration =
+            
configurationFactory.createConfiguration(ConnectionControlConfiguration.class, 
paramString);
+        if (!connectionControlConfiguration.isAllowUnsupportedTransport()) {
+            List<String> supportedTransportCodes = 
getMetadata().getSupportedTransportCodes();
+            if (!supportedTransportCodes.contains(transportCode)) {
+                throw new PlcConnectionException(
+                    "Transport '" + transportCode + "' is not supported by 
driver '" + getProtocolCode()
+                        + "'. Supported transports: " + supportedTransportCodes
+                        + ". Set 'allow-unsupported-transport=true' in the 
connection string to use it anyway.");
+            }
+        }
+
 
         // Get the requested transport type.
         Transport<?> transport = 
transportManager.getTransport(transportCode).orElseThrow(
@@ -176,7 +200,6 @@ public abstract class DriverBase implements PlcDriver {
 
         // Initialize the configuration for the transport.
         Class<? extends TransportConfiguration> transportConfigType = 
getTransportConfigurationClass(transport);
-        ConfigurationFactory configurationFactory = new ConfigurationFactory();
         TransportConfiguration transportConfiguration = 
configurationFactory.createPrefixedConfiguration(
             transportConfigType, transportCode, paramString);
 
diff --git 
a/plc4j/spi/drivers/src/main/java/org/apache/plc4x/java/spi/drivers/config/ConnectionControlConfiguration.java
 
b/plc4j/spi/drivers/src/main/java/org/apache/plc4x/java/spi/drivers/config/ConnectionControlConfiguration.java
new file mode 100644
index 0000000000..10cb89acb3
--- /dev/null
+++ 
b/plc4j/spi/drivers/src/main/java/org/apache/plc4x/java/spi/drivers/config/ConnectionControlConfiguration.java
@@ -0,0 +1,65 @@
+/*
+ * 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
+ *
+ *   https://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.spi.drivers.config;
+
+import org.apache.plc4x.java.spi.config.Configuration;
+import org.apache.plc4x.java.spi.config.annotations.ConfigurationParameter;
+import org.apache.plc4x.java.spi.config.annotations.Description;
+import 
org.apache.plc4x.java.spi.config.annotations.defaults.BooleanDefaultValue;
+
+/**
+ * SPI-level, cross-driver connection controls parsed from the 
connection-string parameters.
+ *
+ * <p>Unlike a driver's protocol {@link Configuration} or a transport 
configuration, the options here
+ * are not protocol- or transport-specific — they govern how the SPI core
+ * ({@code DriverBase.getConnection(...)}) establishes <em>any</em> 
connection. They are parsed from the
+ * same connection-string parameter mechanism every other option uses (via
+ * {@code ConfigurationFactory.createConfiguration(...)}).</p>
+ */
+public class ConnectionControlConfiguration implements Configuration {
+
+    /**
+     * When {@code true}, the SPI core skips its check that the selected 
transport is one of the
+     * transports the driver declares it supports, allowing a driver to be 
used with a transport
+     * outside its declared supported set (the behavior that existed before 
that check was added).
+     *
+     * <p>Defaults to {@code false}: by default the supported-transport check 
is enforced, so
+     * accidentally pairing a driver with a transport it does not support (for 
example a {@code tcp}
+     * transport with the S7 driver, which speaks COTP) fails fast at connect 
time. Set this to
+     * {@code true} only when the non-standard pairing is intentional.</p>
+     *
+     * <p>This option only bypasses the <em>driver-supported</em> check. It 
does NOT relax the
+     * pre-existing requirement that the transport be a registered/known 
transport.</p>
+     */
+    @ConfigurationParameter("allow-unsupported-transport")
+    @BooleanDefaultValue(false)
+    @Description("When true, allows using a transport that is not in the 
driver's set of supported "
+        + "transports. Defaults to false, in which case using an unsupported 
transport fails the "
+        + "connection attempt. Only bypasses the driver-supported check, not 
the check that the "
+        + "transport is registered at all.")
+    public boolean allowUnsupportedTransport;
+
+    /**
+     * @return whether the supported-transport check should be skipped for 
this connection.
+     */
+    public boolean isAllowUnsupportedTransport() {
+        return allowUnsupportedTransport;
+    }
+
+}
diff --git 
a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/java/utils/testutils/driver/internal/ConnectionManager.java
 
b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/java/utils/testutils/driver/internal/ConnectionManager.java
index 0e693b155a..5da694fb64 100644
--- 
a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/java/utils/testutils/driver/internal/ConnectionManager.java
+++ 
b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/java/utils/testutils/driver/internal/ConnectionManager.java
@@ -90,6 +90,14 @@ public class ConnectionManager {
                 .collect(Collectors.joining("&"));
             connectionUrl += (parameterString.isEmpty() ? "" : "?" + 
parameterString);
 
+            // The driver test-suite intentionally drives every driver over 
the synthetic "test"
+            // transport (to replay recorded bytes), which is not in any 
driver's declared
+            // supported-transport set. Opt out of the SPI supported-transport 
check
+            // for that case so the replay connects exactly as before that 
check existed.
+            if ("test".equals(transport)) {
+                connectionUrl += (connectionUrl.contains("?") ? "&" : "?") + 
"allow-unsupported-transport=true";
+            }
+
             LOGGER.debug("Creating connection with URL: {}", connectionUrl);
 
             // Load driver using ServiceLoader

Reply via email to