Rewire SwitchConnectionProvider configuration 60/111760/4
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 20 May 2024 19:19:25 +0000 (21:19 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 20 May 2024 20:59:28 +0000 (22:59 +0200)
Eliminate use of blueprint by replacing it with two components:

OSGiFactorySwitchConnectionConfiguration populates default configuration
into the datastore if not already present.

OSGiSwitchConnectionProviders listens on datastore configuration and
instantiates SwitchConnectionProviders based on observed configuration.

JIRA: OPNFLWPLUG-1129
Change-Id: I7d762b7b07f010d37ba0590515dd30cbcfe3b479
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
openflowjava/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ConnectionConfigurationImpl.java [new file with mode: 0644]
openflowjava/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/SwitchConnectionProviderFactoryImpl.java
openflowjava/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/SwitchConnectionProviderImpl.java
openflowjava/openflowjava-blueprint-config/pom.xml
openflowjava/openflowjava-blueprint-config/src/main/java/org/opendaylight/openflowjava/mdsal/OSGiFactorySwitchConnectionConfiguration.java [new file with mode: 0644]
openflowjava/openflowjava-blueprint-config/src/main/java/org/opendaylight/openflowjava/mdsal/OSGiSwitchConnectionProviders.java [new file with mode: 0644]
openflowjava/openflowjava-blueprint-config/src/main/resources/OSGI-INF/blueprint/openflowjava.xml [deleted file]

diff --git a/openflowjava/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ConnectionConfigurationImpl.java b/openflowjava/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ConnectionConfigurationImpl.java
new file mode 100644 (file)
index 0000000..45a9136
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.openflowjava.protocol.impl.core;
+
+import static java.util.Objects.requireNonNull;
+
+import java.net.InetAddress;
+import java.util.List;
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionConfiguration;
+import org.opendaylight.openflowjava.protocol.api.connection.ThreadConfiguration;
+import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.KeystoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.TransportProtocol;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506.SwitchConnectionConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506._switch.connection.config.Threads;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506._switch.connection.config.Tls;
+
+final class ConnectionConfigurationImpl implements ConnectionConfiguration {
+    private final SwitchConnectionConfig config;
+    private final InetAddress address;
+
+    ConnectionConfigurationImpl(final SwitchConnectionConfig config) {
+        this.config = requireNonNull(config);
+        final var addr = config.getAddress();
+        address = addr != null ? IetfInetUtil.inetAddressFor(addr) : null;
+    }
+
+    @Override
+    public InetAddress getAddress() {
+        return address;
+    }
+
+    @Override
+    public int getPort() {
+        return config.getPort().toJava();
+    }
+
+    @Override
+    public Object getTransferProtocol() {
+        return config.getTransportProtocol();
+    }
+
+    @Override
+    public int getChannelOutboundQueueSize() {
+        return config.getChannelOutboundQueueSize().toJava();
+    }
+
+    @Override
+    public TlsConfiguration getTlsConfiguration() {
+        final Tls tlsConfig = config.getTls();
+        if (tlsConfig == null || !TransportProtocol.TLS.equals(getTransferProtocol())) {
+            return null;
+        }
+
+        return new TlsConfiguration() {
+            @Override
+            public KeystoreType getTlsTruststoreType() {
+                return requireNonNull(tlsConfig.getTruststoreType());
+            }
+
+            @Override
+            public String getTlsTruststore() {
+                return requireNonNull(tlsConfig.getTruststore());
+            }
+
+            @Override
+            public KeystoreType getTlsKeystoreType() {
+                return requireNonNull(tlsConfig.getKeystoreType());
+            }
+
+            @Override
+            public String getTlsKeystore() {
+                return requireNonNull(tlsConfig.getKeystore());
+            }
+
+            @Override
+            public org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.PathType
+                    getTlsKeystorePathType() {
+                return requireNonNull(tlsConfig.getKeystorePathType());
+            }
+
+            @Override
+            public org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.PathType
+                    getTlsTruststorePathType() {
+                return requireNonNull(tlsConfig.getTruststorePathType());
+            }
+
+            @Override
+            public String getKeystorePassword() {
+                return requireNonNull(tlsConfig.getKeystorePassword());
+            }
+
+            @Override
+            public String getCertificatePassword() {
+                return requireNonNull(tlsConfig.getCertificatePassword());
+            }
+
+            @Override
+            public String getTruststorePassword() {
+                return requireNonNull(tlsConfig.getTruststorePassword());
+            }
+
+            @Override
+            public List<String> getCipherSuites() {
+                return tlsConfig.getCipherSuites();
+            }
+        };
+    }
+
+    @Override
+    public long getSwitchIdleTimeout() {
+        return config.getSwitchIdleTimeout().toJava();
+    }
+
+    @Override
+    public Object getSslContext() {
+        return null;
+    }
+
+    @Override
+    public ThreadConfiguration getThreadConfiguration() {
+        final Threads threads = config.getThreads();
+        if (threads == null) {
+            return null;
+        }
+
+        return new ThreadConfiguration() {
+            @Override
+            public int getWorkerThreadCount() {
+                return threads.getWorkerThreads().toJava();
+            }
+
+            @Override
+            public int getBossThreadCount() {
+                return threads.getBossThreads().toJava();
+            }
+        };
+    }
+
+    @Override
+    public boolean useBarrier() {
+        return config.getUseBarrier();
+    }
+
+    @Override
+    public boolean isGroupAddModEnabled() {
+        return config.getGroupAddModEnabled();
+    }
+}
\ No newline at end of file
index 7ee78a13ff930e0dbed1b6c8a6bd18526e5994a7..cb8a963d5ae34062899997d0094a3ca290419d29 100644 (file)
@@ -9,23 +9,12 @@ package org.opendaylight.openflowjava.protocol.impl.core;
 
 import static java.util.Objects.requireNonNull;
 
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.List;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.opendaylight.infrautils.diagstatus.DiagStatusService;
-import org.opendaylight.openflowjava.protocol.api.connection.ConnectionConfiguration;
-import org.opendaylight.openflowjava.protocol.api.connection.ThreadConfiguration;
-import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration;
 import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider;
 import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProviderFactory;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.KeystoreType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.TransportProtocol;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506.SwitchConnectionConfig;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506._switch.connection.config.Threads;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506._switch.connection.config.Tls;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
@@ -48,152 +37,4 @@ public class SwitchConnectionProviderFactoryImpl implements SwitchConnectionProv
     public SwitchConnectionProvider newInstance(final SwitchConnectionConfig config) {
         return new SwitchConnectionProviderImpl(diagStatus, new ConnectionConfigurationImpl(config));
     }
-
-    private static class ConnectionConfigurationImpl implements ConnectionConfiguration {
-        private final SwitchConnectionConfig config;
-        private InetAddress address;
-
-        ConnectionConfigurationImpl(final SwitchConnectionConfig config) {
-            this.config = config;
-
-            try {
-                address = getInetAddress(config.getAddress());
-            } catch (UnknownHostException e) {
-                throw new IllegalStateException(e);
-            }
-        }
-
-        @Override
-        public InetAddress getAddress() {
-            return address;
-        }
-
-        @Override
-        public int getPort() {
-            return config.getPort().toJava();
-        }
-
-        @Override
-        public Object getTransferProtocol() {
-            return config.getTransportProtocol();
-        }
-
-        @Override
-        public int getChannelOutboundQueueSize() {
-            return config.getChannelOutboundQueueSize().toJava();
-        }
-
-        @Override
-        public TlsConfiguration getTlsConfiguration() {
-            final Tls tlsConfig = config.getTls();
-            if (tlsConfig == null || !TransportProtocol.TLS.equals(getTransferProtocol())) {
-                return null;
-            }
-
-            return new TlsConfiguration() {
-                @Override
-                public KeystoreType getTlsTruststoreType() {
-                    return requireNonNull(tlsConfig.getTruststoreType());
-                }
-
-                @Override
-                public String getTlsTruststore() {
-                    return requireNonNull(tlsConfig.getTruststore());
-                }
-
-                @Override
-                public KeystoreType getTlsKeystoreType() {
-                    return requireNonNull(tlsConfig.getKeystoreType());
-                }
-
-                @Override
-                public String getTlsKeystore() {
-                    return requireNonNull(tlsConfig.getKeystore());
-                }
-
-                @Override
-                public org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.PathType
-                        getTlsKeystorePathType() {
-                    return requireNonNull(tlsConfig.getKeystorePathType());
-                }
-
-                @Override
-                public org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.PathType
-                        getTlsTruststorePathType() {
-                    return requireNonNull(tlsConfig.getTruststorePathType());
-                }
-
-                @Override
-                public String getKeystorePassword() {
-                    return requireNonNull(tlsConfig.getKeystorePassword());
-                }
-
-                @Override
-                public String getCertificatePassword() {
-                    return requireNonNull(tlsConfig.getCertificatePassword());
-                }
-
-                @Override
-                public String getTruststorePassword() {
-                    return requireNonNull(tlsConfig.getTruststorePassword());
-                }
-
-                @Override
-                public List<String> getCipherSuites() {
-                    return tlsConfig.getCipherSuites();
-                }
-            };
-        }
-
-        @Override
-        public long getSwitchIdleTimeout() {
-            return config.getSwitchIdleTimeout().toJava();
-        }
-
-        @Override
-        public Object getSslContext() {
-            return null;
-        }
-
-        @Override
-        public ThreadConfiguration getThreadConfiguration() {
-            final Threads threads = config.getThreads();
-            if (threads == null) {
-                return null;
-            }
-
-            return new ThreadConfiguration() {
-                @Override
-                public int getWorkerThreadCount() {
-                    return threads.getWorkerThreads().toJava();
-                }
-
-                @Override
-                public int getBossThreadCount() {
-                    return threads.getBossThreads().toJava();
-                }
-            };
-        }
-
-        @Override
-        public boolean useBarrier() {
-            return config.getUseBarrier();
-        }
-
-        @Override
-        public boolean isGroupAddModEnabled() {
-            return config.getGroupAddModEnabled();
-        }
-
-        private static InetAddress getInetAddress(final IpAddress address) throws UnknownHostException {
-            if (address != null) {
-                if (address.getIpv4Address() != null) {
-                    return InetAddress.getByName(address.getIpv4Address().getValue());
-                } else if (address.getIpv6Address() != null) {
-                    return InetAddress.getByName(address.getIpv6Address().getValue());
-                }
-            }
-            return null;
-        }
-    }
 }
index 78ae05324b5a57a986ce31f780ba3619652a9f66..cf7a880694df5b25f10fe634df547c285e5bc86f 100755 (executable)
@@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.epoll.Epoll;
+import java.util.Map;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.infrautils.diagstatus.DiagStatusService;
 import org.opendaylight.infrautils.diagstatus.ServiceDescriptor;
@@ -57,6 +58,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.meter.band.header.meter.band.MeterBandExperimenterCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.queue.property.header.QueueProperty;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.table.features.properties.grouping.TableFeatureProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506.SwitchConnectionConfig;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -67,11 +73,18 @@ import org.slf4j.LoggerFactory;
  * @author mirehak
  * @author michal.polkorab
  */
+@Component(service = SwitchConnectionProvider.class, factory = SwitchConnectionProviderImpl.FACTORY_NAME)
 public class SwitchConnectionProviderImpl implements SwitchConnectionProvider, ConnectionInitializer {
     private static final Logger LOG = LoggerFactory.getLogger(SwitchConnectionProviderImpl.class);
+
     private static final String THREAD_NAME_PREFIX = "OFP-SwitchConnectionProvider-Udp/TcpHandler";
     private static final String OPENFLOW_JAVA_SERVICE_NAME_PREFIX = "OPENFLOW_SERVER";
 
+    // OSGi DS Component Factory name
+    public static final String FACTORY_NAME =
+        "org.opendaylight.openflowjava.protocol.impl.core.SwitchConnectionProviderImpl";
+    public static final String PROP_CONFIG = ".config";
+
     private SwitchConnectionHandler switchConnectionHandler;
     private ServerFacade serverFacade;
     private final ConnectionConfiguration connConfig;
@@ -107,6 +120,17 @@ public class SwitchConnectionProviderImpl implements SwitchConnectionProvider, C
         deserializationFactory = new DeserializationFactory(deserializerRegistry);
     }
 
+    @Activate
+    public SwitchConnectionProviderImpl(@Reference final DiagStatusService diagStatus,
+            final Map<String, Object> props) {
+        this(diagStatus, new ConnectionConfigurationImpl((SwitchConnectionConfig) props.get(PROP_CONFIG)));
+    }
+
+    @Deactivate
+    void deactivate() {
+        diagReg.close();
+    }
+
     // ID based, on configuration, used for diagstatus serviceIdentifier (ServiceDescriptor moduleServiceName)
     private static String createConnectionSuffix(final @Nullable ConnectionConfiguration config) {
         return config == null ? "-null-config" : "_" + config.getPort();
index 7ac8fe153a89b4815de986df58c41c721eb8856e..cf18698419284ce325c7eb5c8d2ce25f2b561dba 100644 (file)
         <url>https://wiki.opendaylight.org/view/Openflow_Protocol_Library:Main</url>
         <tag>HEAD</tag>
     </scm>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jdt</groupId>
+            <artifactId>org.eclipse.jdt.annotation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-common-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>yang-binding</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin.openflowjava</groupId>
+            <artifactId>openflow-protocol-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin.openflowjava</groupId>
+            <artifactId>openflow-protocol-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin.openflowjava</groupId>
+            <artifactId>openflow-protocol-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.framework</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+        </dependency>
+    </dependencies>
+
     <build>
         <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <configuration>
-                    <instructions>
-                        <DynamicImport-Package>*</DynamicImport-Package>
-                    </instructions>
-                </configuration>
-            </plugin>
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
diff --git a/openflowjava/openflowjava-blueprint-config/src/main/java/org/opendaylight/openflowjava/mdsal/OSGiFactorySwitchConnectionConfiguration.java b/openflowjava/openflowjava-blueprint-config/src/main/java/org/opendaylight/openflowjava/mdsal/OSGiFactorySwitchConnectionConfiguration.java
new file mode 100644 (file)
index 0000000..e567365
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.openflowjava.mdsal;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.List;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.KeystoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.PathType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.TransportProtocol;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506.SwitchConnectionConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506.SwitchConnectionConfigBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506._switch.connection.config.TlsBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A component responsible for populating default (factory) configuration.
+ */
+@Component(service = { })
+public final class OSGiFactorySwitchConnectionConfiguration {
+    private static final Logger LOG = LoggerFactory.getLogger(OSGiFactorySwitchConnectionConfiguration.class);
+
+    @Activate
+    public OSGiFactorySwitchConnectionConfiguration(@Reference final DataBroker dataBroker) {
+        // Common for both cases
+        final var builder = new SwitchConnectionConfigBuilder()
+            .setTransportProtocol(TransportProtocol.TCP)
+            .setGroupAddModEnabled(Boolean.FALSE)
+            .setChannelOutboundQueueSize(Uint16.valueOf(1024))
+            .setTls(new TlsBuilder()
+                .setKeystore("configuration/ssl/ctl.jks")
+                .setKeystoreType(KeystoreType.JKS)
+                .setKeystorePathType(PathType.PATH)
+                .setKeystorePassword("opendaylight")
+                .setTruststore("configuration/ssl/truststore.jks")
+                .setTruststoreType(KeystoreType.JKS)
+                .setTruststorePathType(PathType.PATH)
+                .setTruststorePassword("opendaylight")
+                .setCertificatePassword("opendaylight")
+                .setCipherSuites(List.of())
+                .build());
+
+        // Create OF switch connection provider on port 6653 (default)
+        writeIfNotPresent(dataBroker, builder
+            .setInstanceName("openflow-switch-connection-provider-default-impl")
+            .setPort(Uint16.valueOf(6653))
+            .build());
+        // Create OF switch connection provider on port 6633 (legacy)
+        writeIfNotPresent(dataBroker, builder
+            .setInstanceName("openflow-switch-connection-provider-legacy-impl")
+            .setPort(Uint16.valueOf(6633))
+            .build());
+    }
+
+    private static void writeIfNotPresent(final DataBroker dataBroker, final SwitchConnectionConfig config) {
+        final var instanceName = config.getInstanceName();
+        LOG.info("Checking presence of configuration for {}", instanceName);
+
+        final var sw = Stopwatch.createStarted();
+        final var iid = InstanceIdentifier.builder(SwitchConnectionConfig.class, config.key()).build();
+        final var tx = dataBroker.newReadWriteTransaction();
+        tx.exists(LogicalDatastoreType.CONFIGURATION, iid).addCallback(new FutureCallback<Boolean>() {
+            @Override
+            public void onSuccess(final Boolean result) {
+                LOG.debug("Presence of configuration for {} ascertained in {}", instanceName, sw);
+                if (result) {
+                    LOG.info("Configuration for {} already present", instanceName);
+                    tx.cancel();
+                    return;
+                }
+
+                tx.put(LogicalDatastoreType.CONFIGURATION, iid, config);
+                tx.commit().addCallback(new FutureCallback<CommitInfo>() {
+                    @Override
+                    public void onSuccess(final CommitInfo result) {
+                        LOG.info("Configuration for {} set to factory-default", instanceName);
+                    }
+
+                    @Override
+                    public void onFailure(final Throwable cause) {
+                        LOG.warn("Failed to set configuration for {} set to factory-default", instanceName, cause);
+                    }
+                }, MoreExecutors.directExecutor());
+            }
+
+            @Override
+            public void onFailure(final Throwable cause) {
+                LOG.warn("Failed to ascertain presence of configuration for {} after {}", instanceName, sw, cause);
+                tx.cancel();
+            }
+        }, MoreExecutors.directExecutor());
+    }
+}
diff --git a/openflowjava/openflowjava-blueprint-config/src/main/java/org/opendaylight/openflowjava/mdsal/OSGiSwitchConnectionProviders.java b/openflowjava/openflowjava-blueprint-config/src/main/java/org/opendaylight/openflowjava/mdsal/OSGiSwitchConnectionProviders.java
new file mode 100644 (file)
index 0000000..4d77533
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.openflowjava.mdsal;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.openflowjava.protocol.impl.core.SwitchConnectionProviderImpl;
+import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506.SwitchConnectionConfig;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.service.component.ComponentFactory;
+import org.osgi.service.component.ComponentInstance;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A component exposing {@link SwitchConnectionProvider} into OSGi service registry based on MD-SAL's configuration
+ * data store contents of {@link SwitchConnectionConfig}.
+ */
+@Component(service = { })
+public final class OSGiSwitchConnectionProviders implements DataTreeChangeListener<SwitchConnectionConfig> {
+    private static final Logger LOG = LoggerFactory.getLogger(OSGiSwitchConnectionProviders.class);
+
+    private final Map<String, ComponentInstance<SwitchConnectionProvider>> instances = new HashMap<>();
+    private final ComponentFactory<SwitchConnectionProvider> providerFactory;
+    private final Registration reg;
+
+    @Activate
+    public OSGiSwitchConnectionProviders(@Reference final DataBroker dataBroker,
+            @Reference(target = "(component.factory=" + SwitchConnectionProviderImpl.FACTORY_NAME + ")")
+            final ComponentFactory<SwitchConnectionProvider> providerFactory) {
+        this.providerFactory = requireNonNull(providerFactory);
+        reg = dataBroker.registerTreeChangeListener(DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION,
+            InstanceIdentifier.create(SwitchConnectionConfig.class)), this);
+        LOG.info("MD-SAL configuration-based SwitchConnectionProviders started");
+    }
+
+    @Deactivate
+    synchronized void deactivate() {
+        LOG.info("MD-SAL configuration-based SwitchConnectionProviders stopping");
+        reg.close();
+        instances.forEach((key, instance) -> instance.dispose());
+        instances.clear();
+        LOG.info("MD-SAL configuration-based SwitchConnectionProviders stopped");
+    }
+
+    @Override
+    public synchronized void onDataTreeChanged(final List<DataTreeModification<SwitchConnectionConfig>> changes) {
+        final var apply = new HashMap<String, SwitchConnectionConfig>();
+
+        for (var change : changes) {
+            final var root = change.getRootNode();
+            switch (root.modificationType()) {
+                case DELETE -> apply.put(root.dataBefore().getInstanceName(), null);
+                case SUBTREE_MODIFIED, WRITE -> {
+                    final var after = root.dataAfter();
+                    apply.put(after.getInstanceName(), after);
+                }
+                default -> LOG.warn("Ignoring unhandled root {}", root);
+            }
+        }
+
+        LOG.debug("Applying {} changes", apply.size());
+        apply.entrySet().stream()
+            .sorted(Comparator.comparing(Entry::getKey))
+            .forEach(entry -> {
+                final var type = entry.getKey();
+                final var prev = instances.remove(type);
+                if (prev != null) {
+                    LOG.info("Stopping instance of type '{}'", type);
+                    prev.dispose();
+                }
+
+                final var config = entry.getValue();
+                if (config != null) {
+                    LOG.info("Starting instance of type '{}'", type);
+                    instances.put(type, providerFactory.newInstance(FrameworkUtil.asDictionary(props(type, config))));
+                }
+            });
+    }
+
+    @Override
+    public void onInitialData() {
+        LOG.debug("No configuration is present");
+    }
+
+    private static Map<String, Object> props(final String type, final @Nullable SwitchConnectionConfig config) {
+        return config != null ? Map.of("type", type, SwitchConnectionProviderImpl.PROP_CONFIG, config)
+            : Map.of("type", type);
+    }
+}
diff --git a/openflowjava/openflowjava-blueprint-config/src/main/resources/OSGI-INF/blueprint/openflowjava.xml b/openflowjava/openflowjava-blueprint-config/src/main/resources/OSGI-INF/blueprint/openflowjava.xml
deleted file mode 100644 (file)
index 0b8dffa..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">
-  <reference id="switchConnectionProviderFactory" interface="org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProviderFactory"/>
-  <!-- Create OF switch connection provider on port 6653 (default) -->
-  <odl:clustered-app-config id="defaultSwitchConnConfig" default-config-file-name="default-openflow-connection-config.xml"
-      binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506.SwitchConnectionConfig"
-      list-key-value="openflow-switch-connection-provider-default-impl">
-  </odl:clustered-app-config>
-  <bean id="defaultSwitchConnProvider" factory-ref="switchConnectionProviderFactory" factory-method="newInstance">
-    <argument ref="defaultSwitchConnConfig"/>
-  </bean>
-  <service ref="defaultSwitchConnProvider" interface="org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider"
-          odl:type="openflow-switch-connection-provider-default-impl"/>
-
-  <!-- Create OF switch connection provider on port 6633 (legacy) -->
-  <odl:clustered-app-config id="legacySwitchConnConfig" default-config-file-name="legacy-openflow-connection-config.xml"
-      binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow._switch.connection.config.rev160506.SwitchConnectionConfig"
-      list-key-value="openflow-switch-connection-provider-legacy-impl">
-  </odl:clustered-app-config>
-  <bean id="legacySwitchConnProvider" factory-ref="switchConnectionProviderFactory" factory-method="newInstance">
-    <argument ref="legacySwitchConnConfig"/>
-  </bean>
-  <service ref="legacySwitchConnProvider" interface="org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider"
-          odl:type="openflow-switch-connection-provider-legacy-impl"/>
-</blueprint>