Convert netconf-config to OSGi DS 87/104287/16
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 6 Feb 2023 18:37:10 +0000 (19:37 +0100)
committerRobert Varga <nite@hq.sk>
Wed, 8 Feb 2023 12:40:40 +0000 (12:40 +0000)
Convert blueprint to explicit OSGi DS wiring, retaining the API and
behaviour of the wiring. This requires use of ComponentFactories to
deal with the fact the configuration is in a single configurationPid,
but really ends up being configuration of three inter-dependend classes.

JIRA: NETCONF-950
Change-Id: I19d506a336483bf0ea06a230ef5108a35d8845cc
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
netconf/netconf-config/pom.xml
netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/Configuration.java [new file with mode: 0644]
netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfConfiguration.java [new file with mode: 0644]
netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfProcessingExecutor.java [new file with mode: 0644]
netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfSshScheduledExecutor.java [new file with mode: 0644]
netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfThreadFactory.java [new file with mode: 0644]
netconf/netconf-config/src/main/resources/OSGI-INF/blueprint/netconf-config.xml [deleted file]

index 73babc6b428441590716d1abaeafc4912470033a..1fc1253b21e27d740278eb8d2dda49a4e9bb69b1 100644 (file)
@@ -7,18 +7,57 @@
  and is available at http://www.eclipse.org/legal/epl-v10.html
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.netconf</groupId>
+        <artifactId>netconf-parent</artifactId>
+        <version>5.0.2-SNAPSHOT</version>
+        <relativePath>../../parent</relativePath>
+    </parent>
 
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.opendaylight.odlparent</groupId>
-    <artifactId>bundle-parent</artifactId>
-    <version>12.0.2</version>
-    <relativePath/>
-  </parent>
+    <artifactId>netconf-config</artifactId>
+    <description>Global threadpools for NETCONF</description>
+    <packaging>bundle</packaging>
 
-  <groupId>org.opendaylight.netconf</groupId>
-  <artifactId>netconf-config</artifactId>
-  <version>5.0.2-SNAPSHOT</version>
-  <description>Configuration files for netconf</description>
-  <packaging>bundle</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>threadpool-config-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>threadpool-config-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.guicedee.services</groupId>
+            <artifactId>javax.inject</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </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>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+        </dependency>
+    </dependencies>
 </project>
diff --git a/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/Configuration.java b/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/Configuration.java
new file mode 100644 (file)
index 0000000..fbe7be7
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 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.netconf.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+@NonNullByDefault
+@ObjectClassDefinition
+public @interface Configuration {
+    @AttributeDefinition(min = "1")
+    String name$_$prefix() default GlobalNetconfThreadFactory.DEFAULT_NAME_PREFIX;
+    @AttributeDefinition(min = "0")
+    int min$_$thread$_$count$_$flexible$_$thread$_$pool()
+        default GlobalNetconfProcessingExecutor.DEFAULT_MIN_THREAD_COUNT;
+    @AttributeDefinition(min = "1")
+    int max$_$thread$_$count$_$flexible$_$thread$_$pool()
+        default GlobalNetconfProcessingExecutor.DEFAULT_MAX_THREAD_COUNT;
+    @AttributeDefinition(min = "0")
+    long keep$_$alive$_$millis$_$flexible$_$thread$_$pool()
+        default GlobalNetconfProcessingExecutor.DEFAULT_KEEPALIVE_MILLIS;
+    @AttributeDefinition(min = "1")
+    int max$_$thread$_$count$_$scheduled$_$thread$_$pool()
+        default GlobalNetconfSshScheduledExecutor.DEFAULT_MAX_THREAD_COUNT;
+}
diff --git a/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfConfiguration.java b/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfConfiguration.java
new file mode 100644 (file)
index 0000000..0d0bfd4
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2023 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.netconf.config;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.Map;
+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.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Component instantiating global NETCONF resources.
+ */
+@Component(service = { }, configurationPid = "org.opendaylight.netconf.config")
+@Designate(ocd = Configuration.class)
+public final class GlobalNetconfConfiguration {
+    private static final Logger LOG = LoggerFactory.getLogger(GlobalNetconfConfiguration.class);
+
+    private final ComponentFactory<GlobalNetconfProcessingExecutor> processingFactory;
+    private final ComponentFactory<GlobalNetconfSshScheduledExecutor> sshScheduledFactory;
+
+    private GlobalNetconfThreadFactory threadFactory;
+    private ComponentInstance<GlobalNetconfProcessingExecutor> processingExecutor;
+    private Map<String, ?> processingProps;
+    private ComponentInstance<GlobalNetconfSshScheduledExecutor> sshScheduledExecutor;
+    private Map<String, ?> sshScheduledProps;
+
+    @Activate
+    public GlobalNetconfConfiguration(
+            @Reference(target = "(component.factory=" + GlobalNetconfProcessingExecutor.FACTORY_NAME + ")")
+            final ComponentFactory<GlobalNetconfProcessingExecutor> processingFactory,
+            @Reference(target = "(component.factory=" + GlobalNetconfSshScheduledExecutor.FACTORY_NAME + ")")
+            final ComponentFactory<GlobalNetconfSshScheduledExecutor> sshScheduledFactory,
+            final Configuration configuration) {
+        this.processingFactory = requireNonNull(processingFactory);
+        this.sshScheduledFactory = requireNonNull(sshScheduledFactory);
+
+        threadFactory = new GlobalNetconfThreadFactory(configuration.name$_$prefix());
+        processingProps = GlobalNetconfProcessingExecutor.props(threadFactory, configuration);
+        processingExecutor = processingFactory.newInstance(FrameworkUtil.asDictionary(processingProps));
+        sshScheduledProps = GlobalNetconfSshScheduledExecutor.props(threadFactory, configuration);
+        sshScheduledExecutor = sshScheduledFactory.newInstance(FrameworkUtil.asDictionary(sshScheduledProps));
+        LOG.info("Global NETCONF configuration pools started");
+    }
+
+    @Modified
+    void modified(final Configuration configuration) {
+        final var newNamePrefix = configuration.name$_$prefix();
+        if (!threadFactory.getNamePrefix().equals(newNamePrefix)) {
+            threadFactory = new GlobalNetconfThreadFactory(newNamePrefix);
+            processingProps = null;
+            sshScheduledProps = null;
+            LOG.debug("Forcing restart of all executors");
+        }
+
+        // We want to instantiate new services before we dispose old ones, so
+        final var toDispose = new ArrayList<ComponentInstance<?>>();
+
+        final var newProcessingProps = GlobalNetconfProcessingExecutor.props(threadFactory, configuration);
+        if (!newProcessingProps.equals(processingProps)) {
+            processingProps = newProcessingProps;
+            toDispose.add(processingExecutor);
+            processingExecutor = processingFactory.newInstance(FrameworkUtil.asDictionary(processingProps));
+            LOG.debug("Processing executor restarted with {}", processingProps);
+        }
+
+        final var newSshScheduledProps = GlobalNetconfSshScheduledExecutor.props(threadFactory, configuration);
+        if (!newSshScheduledProps.equals(sshScheduledProps)) {
+            sshScheduledProps = newSshScheduledProps;
+            toDispose.add(sshScheduledExecutor);
+            sshScheduledExecutor = sshScheduledFactory.newInstance(FrameworkUtil.asDictionary(sshScheduledProps));
+            LOG.debug("Scheduled executor restarted with {}", sshScheduledProps);
+        }
+
+        toDispose.forEach(ComponentInstance::dispose);
+    }
+
+    @Deactivate
+    void deactivate() {
+        processingExecutor.dispose();
+        processingExecutor = null;
+        sshScheduledExecutor.dispose();
+        sshScheduledExecutor = null;
+        threadFactory = null;
+        LOG.info("Global NETCONF configuration pools stopped");
+    }
+
+    static <T> T extractProp(final Map<String, ?> properties, final String key, final Class<T> valueType) {
+        return valueType.cast(verifyNotNull(properties.get(requireNonNull(key))));
+    }
+}
diff --git a/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfProcessingExecutor.java b/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfProcessingExecutor.java
new file mode 100644 (file)
index 0000000..cdde2ca
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023 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.netconf.config;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.opendaylight.controller.config.threadpool.ThreadPool;
+import org.opendaylight.controller.config.threadpool.util.FlexibleThreadPoolWrapper;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+
+@Singleton
+@Component(factory = GlobalNetconfProcessingExecutor.FACTORY_NAME, service = ThreadPool.class)
+public final class GlobalNetconfProcessingExecutor extends FlexibleThreadPoolWrapper {
+    public static final String OSGI_TYPE = "global-netconf-processing-executor";
+    public static final int DEFAULT_MIN_THREAD_COUNT = 1;
+    public static final int DEFAULT_MAX_THREAD_COUNT = 4;
+    public static final long DEFAULT_KEEPALIVE_MILLIS = 600000;
+
+    // OSGi DS Component Factory name
+    static final String FACTORY_NAME = "org.opendaylight.netconf.config.GlobalNetconfProcessingExecutor";
+
+    private static final String PROP_KEEPALIVE = ".keepAlive";
+    private static final String PROP_MIN_THREAD_COUNT = ".minThreadCount";
+    private static final String PROP_MAX_THREAD_COUNT = ".maxThreadCount";
+    private static final String PROP_THREAD_FACTORY = ".threadFactory";
+
+    public GlobalNetconfProcessingExecutor(final GlobalNetconfThreadFactory threadFactory, final int minThreadCount,
+            final int maxThreadCount, final long keepAliveMillis) {
+        super(minThreadCount, maxThreadCount, keepAliveMillis, TimeUnit.MILLISECONDS, threadFactory);
+    }
+
+    @Inject
+    public GlobalNetconfProcessingExecutor(final GlobalNetconfThreadFactory threadFactory) {
+        this(threadFactory, DEFAULT_MIN_THREAD_COUNT, DEFAULT_MAX_THREAD_COUNT, DEFAULT_KEEPALIVE_MILLIS);
+    }
+
+    @Activate
+    public GlobalNetconfProcessingExecutor(final Map<String, ?> properties) {
+        this(GlobalNetconfConfiguration.extractProp(properties, PROP_THREAD_FACTORY, GlobalNetconfThreadFactory.class),
+            GlobalNetconfConfiguration.extractProp(properties, PROP_MIN_THREAD_COUNT, Integer.class),
+            GlobalNetconfConfiguration.extractProp(properties, PROP_MAX_THREAD_COUNT, Integer.class),
+            GlobalNetconfConfiguration.extractProp(properties, PROP_KEEPALIVE, Long.class));
+    }
+
+    @Override
+    @PreDestroy
+    @Deactivate
+    public void close() {
+        super.close();
+    }
+
+    static Map<String, ?> props(final GlobalNetconfThreadFactory threadFactory, final Configuration configuration) {
+        return Map.of(
+            "type", OSGI_TYPE,
+            PROP_THREAD_FACTORY, requireNonNull(threadFactory),
+            PROP_KEEPALIVE, configuration.keep$_$alive$_$millis$_$flexible$_$thread$_$pool(),
+            PROP_MIN_THREAD_COUNT, configuration.min$_$thread$_$count$_$flexible$_$thread$_$pool(),
+            PROP_MAX_THREAD_COUNT, configuration.max$_$thread$_$count$_$flexible$_$thread$_$pool());
+    }
+}
diff --git a/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfSshScheduledExecutor.java b/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfSshScheduledExecutor.java
new file mode 100644 (file)
index 0000000..29e0bfc
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 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.netconf.config;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Map;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
+import org.opendaylight.controller.config.threadpool.util.ScheduledThreadPoolWrapper;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+
+@Singleton
+@Component(factory = GlobalNetconfSshScheduledExecutor.FACTORY_NAME, service = ScheduledThreadPool.class)
+public final class GlobalNetconfSshScheduledExecutor extends ScheduledThreadPoolWrapper {
+    public static final String OSGI_TYPE = "global-netconf-ssh-scheduled-executor";
+    public static final int DEFAULT_MAX_THREAD_COUNT = 8;
+
+    // OSGi DS Component Factory name
+    static final String FACTORY_NAME = "org.opendaylight.netconf.config.GlobalNetconfSshScheduledExecutor";
+
+    private static final String PROP_MAX_THREAD_COUNT = ".maxThreadCount";
+    private static final String PROP_THREAD_FACTORY = ".threadFactory";
+
+    public GlobalNetconfSshScheduledExecutor(final GlobalNetconfThreadFactory threadFactory, final int maxThreadCount) {
+        super(maxThreadCount, threadFactory);
+    }
+
+    @Inject
+    public GlobalNetconfSshScheduledExecutor(final GlobalNetconfThreadFactory threadFactory) {
+        this(threadFactory, DEFAULT_MAX_THREAD_COUNT);
+    }
+
+    @Activate
+    public GlobalNetconfSshScheduledExecutor(final Map<String, ?> properties) {
+        this(GlobalNetconfConfiguration.extractProp(properties, PROP_THREAD_FACTORY, GlobalNetconfThreadFactory.class),
+            GlobalNetconfConfiguration.extractProp(properties, PROP_MAX_THREAD_COUNT, Integer.class));
+    }
+
+    @Override
+    @PreDestroy
+    @Deactivate
+    public void close() {
+        super.close();
+    }
+
+    static Map<String, ?> props(final GlobalNetconfThreadFactory threadFactory, final Configuration configuration) {
+        return Map.of(
+            "type", OSGI_TYPE,
+            PROP_MAX_THREAD_COUNT, configuration.max$_$thread$_$count$_$scheduled$_$thread$_$pool(),
+            PROP_THREAD_FACTORY, requireNonNull(threadFactory));
+    }
+}
diff --git a/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfThreadFactory.java b/netconf/netconf-config/src/main/java/org/opendaylight/netconf/config/GlobalNetconfThreadFactory.java
new file mode 100644 (file)
index 0000000..1c06140
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023 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.netconf.config;
+
+import javax.inject.Inject;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.controller.config.threadpool.util.NamingThreadPoolFactory;
+
+/**
+ * Shared {@link NamingThreadPoolFactory} for {@link GlobalNetconfProcessingExecutor} and
+ * {@link GlobalNetconfSshScheduledExecutor}.
+ */
+@NonNullByDefault
+public final class GlobalNetconfThreadFactory extends NamingThreadPoolFactory {
+    public static final String DEFAULT_NAME_PREFIX = "remote-connector-processing-executor";
+
+    public GlobalNetconfThreadFactory(final String namePrefix) {
+        super(namePrefix);
+    }
+
+    @Inject
+    public GlobalNetconfThreadFactory() {
+        this(DEFAULT_NAME_PREFIX);
+    }
+}
diff --git a/netconf/netconf-config/src/main/resources/OSGI-INF/blueprint/netconf-config.xml b/netconf/netconf-config/src/main/resources/OSGI-INF/blueprint/netconf-config.xml
deleted file mode 100644 (file)
index 5c01b16..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (c) 2016 Inocybe Technologies Inc. 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
--->
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
-           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.3.0"
-           odl:restart-dependents-on-updates="true">
-
-    <!-- Default configuration for thread pools -->
-
-    <cm:property-placeholder persistent-id="org.opendaylight.netconf.config"
-                             update-strategy="none">
-        <cm:default-properties>
-            <cm:property name="name-prefix" value="remote-connector-processing-executor"/>
-            <cm:property name="min-thread-count-flexible-thread-pool" value="1"/>
-            <cm:property name="max-thread-count-flexible-thread-pool" value="4"/>
-            <cm:property name="keep-alive-millis-flexible-thread-pool" value="600000"/>
-            <cm:property name="max-thread-count-scheduled-thread-pool" value="8"/>
-        </cm:default-properties>
-    </cm:property-placeholder>
-
-    <!-- Naming Thread Factory -->
-
-    <bean id="namingThreadPoolFactory"
-          class="org.opendaylight.controller.config.threadpool.util.NamingThreadPoolFactory">
-        <argument value="${name-prefix}"/>
-    </bean>
-
-    <!-- Flexible Thread Pool -->
-
-    <bean id="timeUnitMilli" class="java.util.concurrent.TimeUnit"
-          factory-method="valueOf">
-        <argument value="MILLISECONDS"/>
-    </bean>
-
-    <bean id="flexibleThreadPool"
-          class="org.opendaylight.controller.config.threadpool.util.FlexibleThreadPoolWrapper">
-        <argument value="${min-thread-count-flexible-thread-pool}"/>
-        <argument value="${max-thread-count-flexible-thread-pool}"/>
-        <argument value="${keep-alive-millis-flexible-thread-pool}"/>
-        <argument ref="timeUnitMilli"/>
-        <argument ref="namingThreadPoolFactory"/>
-    </bean>
-    <service ref="flexibleThreadPool"
-             interface="org.opendaylight.controller.config.threadpool.ThreadPool"
-             odl:type="global-netconf-processing-executor"/>
-
-    <!-- Scheduled Thread Pool -->
-
-    <bean id="scheduleThreadPool"
-          class="org.opendaylight.controller.config.threadpool.util.ScheduledThreadPoolWrapper">
-        <argument value="${max-thread-count-scheduled-thread-pool}"/>
-        <argument ref="namingThreadPoolFactory"/>
-    </bean>
-    <service ref="scheduleThreadPool"
-             interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
-             odl:type="global-netconf-ssh-scheduled-executor"/>
-
-</blueprint>