Rewire SwitchConnectionProvider configuration
[openflowplugin.git] / openflowjava / openflowjava-blueprint-config / src / main / java / org / opendaylight / openflowjava / mdsal / OSGiSwitchConnectionProviders.java
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);
+    }
+}