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
