Remove NetconfTopology 90/106790/4
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 3 Jul 2023 20:01:44 +0000 (22:01 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 3 Jul 2023 20:16:49 +0000 (22:16 +0200)
Align Node lifecycle with its users, keeping configuration updates
internal to AbstractNetconfTopology. Also hide NetconfConnectorDTO.

JIRA: NETCONF-1069
Change-Id: If8a1821171d5e52ba84cd317a01588a3f69c5bd6
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
apps/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java
apps/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java
apps/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java
apps/netconf-topology-impl/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java
apps/netconf-topology-impl/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java
apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyContext.java
apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologySingletonImpl.java
apps/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/NetconfTopology.java [deleted file]
apps/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java
apps/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/NetconfConnectorDTO.java

index 861d3e217c5af0e80db4b3d6f9b5a5c631ba69f1..86ae0725c2091bd2afb48ec8971afd0d2316c099 100644 (file)
@@ -33,7 +33,6 @@ import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
 import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
 import org.opendaylight.netconf.nettyutil.ReconnectFuture;
 import org.opendaylight.netconf.topology.spi.NetconfNodeUtils;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -138,10 +137,9 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom
             topology.disconnectNode(nodeId);
         });
         if (deviceContext != null) {
-            final NodeId nodeId = deviceContext.getId();
             final Node configNode = deviceContext.getConfigNode();
             LOG.info("Provisioning fake config {}", configNode);
-            topology.connectNode(nodeId, configNode);
+            topology.connectNode(configNode);
         }
     }
 
index 7f0c98884f71e21e4d661e65a3783cbdfa7221cb..d8efc31872ca723032d370a8360f051246d6c433 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.netconf.callhome.mount;
 
+import com.google.common.annotations.VisibleForTesting;
 import io.netty.util.concurrent.EventExecutor;
 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
@@ -20,6 +21,8 @@ import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
 import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
 import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
 import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 
 // Non-final for mocking
 public class CallHomeTopology extends AbstractNetconfTopology {
@@ -34,4 +37,13 @@ public class CallHomeTopology extends AbstractNetconfTopology {
             schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, deviceActionFactory,
             baseSchemas, credentialProvider, sslHandlerFactoryProvider);
     }
+
+    void disconnectNode(final NodeId nodeId) {
+        deleteNode(nodeId);
+    }
+
+    @VisibleForTesting
+    public void connectNode(final Node node) {
+        ensureNode(node);
+    }
 }
index 2bf9ee3f2c5b9c8fa0882077a663f172d75da9fa..b4c8a659fd6e4ce929599fad2b641d6bd7fc982d 100644 (file)
@@ -149,6 +149,6 @@ public class CallHomeMountDispatcherTest {
         // when
         instance.onNetconfSubsystemOpened(mockProtoSess, activator);
         // then
-        verify(instance.topology, times(1)).connectNode(any(NodeId.class), any(Node.class));
+        verify(instance.topology, times(1)).connectNode(any(Node.class));
     }
 }
index 4c4488ce54b9cd79963e8050610ef8987c92c388..3a61b1fd477d527be04d64e5de7f9cdeb123d200 100644 (file)
@@ -32,7 +32,6 @@ import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
 import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
 import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
 import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology;
-import org.opendaylight.netconf.topology.spi.NetconfConnectorDTO;
 import org.opendaylight.netconf.topology.spi.NetconfNodeUtils;
 import org.opendaylight.netconf.topology.spi.NetconfTopologyRPCProvider;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNodeTopologyService;
@@ -127,10 +126,7 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology
         }
 
         // close all existing connectors, delete whole topology in datastore?
-        for (final NetconfConnectorDTO connectorDTO : activeConnectors.values()) {
-            connectorDTO.close();
-        }
-        activeConnectors.clear();
+        deleteAllNodes();
 
         if (dtclReg != null) {
             dtclReg.close();
@@ -144,28 +140,20 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology
             final DataObjectModification<Node> rootNode = change.getRootNode();
             final NodeId nodeId;
             switch (rootNode.getModificationType()) {
-                case SUBTREE_MODIFIED:
-                    nodeId = getNodeId(rootNode.getIdentifier());
-                    LOG.debug("Config for node {} updated", nodeId);
-                    disconnectNode(nodeId);
-                    connectNode(nodeId, rootNode.getDataAfter());
-                    break;
-                case WRITE:
-                    nodeId = getNodeId(rootNode.getIdentifier());
-                    LOG.debug("Config for node {} created", nodeId);
-                    if (activeConnectors.containsKey(nodeId)) {
-                        LOG.warn("RemoteDevice{{}} was already configured, reconfiguring..", nodeId);
-                        disconnectNode(nodeId);
-                    }
-                    connectNode(nodeId, rootNode.getDataAfter());
-                    break;
-                case DELETE:
+                case SUBTREE_MODIFIED -> {
+                    LOG.debug("Config for node {} updated", getNodeId(rootNode.getIdentifier()));
+                    ensureNode(rootNode.getDataAfter());
+                }
+                case WRITE -> {
+                    LOG.debug("Config for node {} created", getNodeId(rootNode.getIdentifier()));
+                    ensureNode(rootNode.getDataAfter());
+                }
+                case DELETE -> {
                     nodeId = getNodeId(rootNode.getIdentifier());
                     LOG.debug("Config for node {} deleted", nodeId);
-                    disconnectNode(nodeId);
-                    break;
-                default:
-                    LOG.debug("Unsupported modification type: {}.", rootNode.getModificationType());
+                    deleteNode(nodeId);
+                }
+                default -> LOG.debug("Unsupported modification type: {}.", rootNode.getModificationType());
             }
         }
     }
index 65f289b1f79c0e96489b19d2aa9091bd97d48b39..7c37b77e0fef279f41bf858c893d76317fec2c3f 100644 (file)
@@ -19,8 +19,6 @@ import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import io.netty.util.concurrent.EventExecutor;
 import java.util.Collection;
@@ -73,7 +71,6 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.common.Decimal64;
-import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.common.Uint16;
 import org.opendaylight.yangtools.yang.common.Uint32;
 import org.opendaylight.yangtools.yang.parser.api.YangParserException;
@@ -160,18 +157,17 @@ public class NetconfTopologyImplTest {
         doReturn(newNode).when(ch).getRootNode();
         changes.add(ch);
         spyTopology.onDataTreeChanged(changes);
-        verify(spyTopology).connectNode(NetconfTopologyImpl.getNodeId(pa), nn.build());
+        verify(spyTopology).ensureNode(nn.build());
 
         doReturn(DataObjectModification.ModificationType.DELETE).when(newNode).getModificationType();
         spyTopology.onDataTreeChanged(changes);
-        verify(spyTopology).disconnectNode(NetconfTopologyImpl.getNodeId(pa));
+        verify(spyTopology).deleteNode(NetconfTopologyImpl.getNodeId(pa));
 
         doReturn(DataObjectModification.ModificationType.SUBTREE_MODIFIED).when(newNode).getModificationType();
         spyTopology.onDataTreeChanged(changes);
 
-        //one in previous creating and deleting node and one in updating
-        verify(spyTopology, times(2)).disconnectNode(NetconfTopologyImpl.getNodeId(pa));
-        verify(spyTopology, times(2)).connectNode(NetconfTopologyImpl.getNodeId(pa), nn.build());
+        // one in previous creating and deleting node and one in updating
+        verify(spyTopology, times(2)).ensureNode(nn.build());
     }
 
     @Test
@@ -246,13 +242,13 @@ public class NetconfTopologyImplTest {
         }
 
         @Override
-        public ListenableFuture<Empty> connectNode(final NodeId nodeId, final Node configNode) {
-            return Futures.immediateFuture(Empty.value());
+        public void ensureNode(final Node configNode) {
+            // No-op
         }
 
         @Override
-        public ListenableFuture<Empty> disconnectNode(final NodeId nodeId) {
-            return Futures.immediateFuture(Empty.value());
+        public void deleteNode(final NodeId nodeId) {
+            // No-op
         }
     }
 
index 91f4ecf4056f5b2d528c1f4b358bcb30cf8ea2d6..7c54c21e4019a3cad9b1ed424376073c5008e16e 100644 (file)
@@ -55,10 +55,10 @@ class NetconfTopologyContext implements ClusterSingletonService, AutoCloseable {
             final ServiceGroupIdentifier serviceGroupIdent, final NetconfTopologySetup setup,
             final CredentialProvider credentialProvider, final SslHandlerFactoryProvider sslHandlerFactoryProvider) {
         this.serviceGroupIdent = requireNonNull(serviceGroupIdent);
-        this.remoteDeviceId = NetconfNodeUtils.toRemoteDeviceId(setup.getNode().getNodeId(),
+        remoteDeviceId = NetconfNodeUtils.toRemoteDeviceId(setup.getNode().getNodeId(),
                 setup.getNode().augmentation(NetconfNode.class));
 
-        this.topologySingleton = new NetconfTopologySingletonImpl(topologyId, clientDispatcher,
+        topologySingleton = new NetconfTopologySingletonImpl(topologyId, clientDispatcher,
                 eventExecutor, keepaliveExecutor, processingExecutor, schemaManager, dataBroker, mountPointService,
                 encryptionService, deviceActionFactory, baseSchemas, remoteDeviceId, setup, actorResponseWaitTime,
                 credentialProvider, sslHandlerFactoryProvider);
@@ -99,7 +99,7 @@ class NetconfTopologyContext implements ClusterSingletonService, AutoCloseable {
         remoteDeviceId = NetconfNodeUtils.toRemoteDeviceId(node.getNodeId(), node.augmentation(NetconfNode.class));
 
         if (isMaster) {
-            getTopologySingleton().disconnectNode(setup.getNode().getNodeId());
+            getTopologySingleton().dropNode(setup.getNode().getNodeId());
             getTopologySingleton().refreshSetupConnection(setup, remoteDeviceId);
         } else {
             getTopologySingleton().refreshDevice(setup, remoteDeviceId);
index a5a8c4342fa66beca83b1cf18eab301f3b293c27..199cfa931e382ae29bab490e6228c43a5c3ab89b 100644 (file)
@@ -31,6 +31,7 @@ import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySet
 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
 import org.opendaylight.netconf.topology.singleton.messages.RefreshSetupMasterActorData;
 import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -73,13 +74,13 @@ final class NetconfTopologySingletonImpl extends AbstractNetconfTopology impleme
                 remoteDeviceId.name(), masterAddress));
 
         // setup connection to device
-        connectNode(setup.getNode().getNodeId(), setup.getNode());
+        ensureNode(setup.getNode());
     }
 
     void becomeTopologyFollower() {
         registerNodeManager();
         // disconnect device from this node and listen for changes from leader
-        disconnectNode(setup.getNode().getNodeId());
+        deleteNode(setup.getNode().getNodeId());
         if (masterActorRef != null) {
             // was leader before
             setup.getActorSystem().stop(masterActorRef);
@@ -115,6 +116,10 @@ final class NetconfTopologySingletonImpl extends AbstractNetconfTopology impleme
         netconfNodeManager.close();
     }
 
+    void dropNode(final NodeId nodeId) {
+        deleteNode(nodeId);
+    }
+
     @Override
     public void close() {
         unregisterNodeManager();
diff --git a/apps/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/NetconfTopology.java b/apps/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/NetconfTopology.java
deleted file mode 100644 (file)
index 32de9bf..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, 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
- */
-package org.opendaylight.netconf.topology.api;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
-import org.opendaylight.yangtools.yang.common.Empty;
-
-public interface NetconfTopology {
-
-    ListenableFuture<Empty> connectNode(NodeId nodeId, Node configNode);
-
-    ListenableFuture<Empty> disconnectNode(NodeId nodeId);
-}
index 9090397cf53adb09d762983541365973b3380c05..fc7d897c23d05f0cb26d4eb76360cbf524166df1 100644 (file)
@@ -54,7 +54,6 @@ import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
 import org.opendaylight.netconf.nettyutil.TimedReconnectStrategyFactory;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
-import org.opendaylight.netconf.topology.api.NetconfTopology;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.connection.parameters.Protocol.Name;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.credentials.Credentials;
@@ -80,9 +79,10 @@ import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public abstract class AbstractNetconfTopology implements NetconfTopology {
+public abstract class AbstractNetconfTopology {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
 
+    private final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
     private final NetconfClientDispatcher clientDispatcher;
     private final EventExecutor eventExecutor;
     private final DeviceActionFactory deviceActionFactory;
@@ -97,7 +97,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
     protected final DOMMountPointService mountPointService;
     protected final String topologyId;
     protected final AAAEncryptionService encryptionService;
-    protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
 
     protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
                                       final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
@@ -139,42 +138,44 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
         LOG.debug("Topology {} initialized", topologyId);
     }
 
-    @Override
-    public ListenableFuture<Empty> connectNode(final NodeId nodeId, final Node configNode) {
-        LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, hideCredentials(configNode));
-        return setupConnection(nodeId, configNode);
+    // Non-final for testing
+    protected void ensureNode(final Node node) {
+        lockedEnsureNode(node);
     }
 
-    /**
-     * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
-     *
-     * @param nodeConfiguration Node configuration container.
-     * @return String representation of node configuration with credentials replaced by asterisks.
-     */
-    @VisibleForTesting
-    public static String hideCredentials(final Node nodeConfiguration) {
-        final NetconfNode netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
-        final String nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
-        final String nodeConfigurationString = nodeConfiguration.toString();
-        return nodeConfigurationString.replace(nodeCredentials, "***");
+    private synchronized void lockedEnsureNode(final Node node) {
+        final var nodeId = node.requireNodeId();
+        final var prev = activeConnectors.remove(nodeId);
+        if (prev != null) {
+            LOG.info("RemoteDevice{{}} was already configured, disconnecting", nodeId);
+            prev.close();
+        }
+
+        LOG.info("Connecting RemoteDevice{{}}, with config {}", nodeId, hideCredentials(node));
+        setupConnection(nodeId, node);
+    }
+
+    // Non-final for testing
+    protected void deleteNode(final NodeId nodeId) {
+        lockedDeleteNode(nodeId);
     }
 
-    @Override
-    public ListenableFuture<Empty> disconnectNode(final NodeId nodeId) {
+    private synchronized void lockedDeleteNode(final NodeId nodeId) {
         final var nodeName = nodeId.getValue();
         LOG.debug("Disconnecting RemoteDevice{{}}", nodeName);
 
-        final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
-        if (connectorDTO == null) {
-            return Futures.immediateFailedFuture(
-                new IllegalStateException("Cannot disconnect " + nodeName + " as it is not connected"));
+        final var connectorDTO = activeConnectors.remove(nodeId);
+        if (connectorDTO != null) {
+            connectorDTO.close();
         }
+    }
 
-        connectorDTO.close();
-        return Futures.immediateFuture(Empty.value());
+    protected final synchronized void deleteAllNodes() {
+        activeConnectors.values().forEach(NetconfConnectorDTO::close);
+        activeConnectors.clear();
     }
 
-    protected ListenableFuture<Empty> setupConnection(final NodeId nodeId, final Node configNode) {
+    protected final void setupConnection(final NodeId nodeId, final Node configNode) {
         final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
         final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
 
@@ -203,8 +204,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
                 // remove this node from active connectors?
             }
         }, MoreExecutors.directExecutor());
-
-        return future;
     }
 
     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
@@ -265,7 +264,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
         return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade, yanglibRegistrations);
     }
 
-    protected RemoteDeviceHandler createSalFacade(RemoteDeviceId deviceId, boolean lockDatastore) {
+    protected RemoteDeviceHandler createSalFacade(final RemoteDeviceId deviceId, final boolean lockDatastore) {
         return new NetconfTopologyDeviceSalFacade(deviceId, mountPointService, lockDatastore, dataBroker);
     }
 
@@ -362,4 +361,18 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
         }
         throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
     }
+
+    /**
+     * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
+     *
+     * @param nodeConfiguration Node configuration container.
+     * @return String representation of node configuration with credentials replaced by asterisks.
+     */
+    @VisibleForTesting
+    public static final String hideCredentials(final Node nodeConfiguration) {
+        final var netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
+        final var nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
+        final var nodeConfigurationString = nodeConfiguration.toString();
+        return nodeConfigurationString.replace(nodeCredentials, "***");
+    }
 }
index 4939cabfede90571c7897b426d6d5ee1122299a3..7da4c9569587a9307cbe8bae75427f192511d591 100644 (file)
@@ -7,33 +7,32 @@
  */
 package org.opendaylight.netconf.topology.spi;
 
+import static java.util.Objects.requireNonNull;
+
 import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator;
 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
 
-public final class NetconfConnectorDTO implements AutoCloseable {
-    private final List<SchemaSourceRegistration<?>> yanglibRegistrations;
-    private final NetconfDeviceCommunicator communicator;
-    private final RemoteDeviceHandler facade;
+final class NetconfConnectorDTO implements AutoCloseable {
+    private final @NonNull List<SchemaSourceRegistration<?>> yanglibRegistrations;
+    private final @NonNull NetconfDeviceCommunicator communicator;
+    private final @NonNull RemoteDeviceHandler facade;
 
-    public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final RemoteDeviceHandler facade,
+    NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final RemoteDeviceHandler facade,
             final List<SchemaSourceRegistration<?>> yanglibRegistrations) {
-        this.communicator = communicator;
-        this.facade = facade;
-        this.yanglibRegistrations = yanglibRegistrations;
+        this.communicator = requireNonNull(communicator);
+        this.facade = requireNonNull(facade);
+        this.yanglibRegistrations = List.copyOf(yanglibRegistrations);
     }
 
-    public NetconfDeviceCommunicator getCommunicator() {
+    NetconfDeviceCommunicator getCommunicator() {
         return communicator;
     }
 
-    public RemoteDeviceHandler getFacade() {
-        return facade;
-    }
-
-    public NetconfClientSessionListener getSessionListener() {
+    NetconfClientSessionListener getSessionListener() {
         return communicator;
     }