Use a switch expression in createClient()
[netconf.git] / protocol / netconf-client / src / main / java / org / opendaylight / netconf / client / NetconfClientFactoryImpl.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.client;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableSet;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.SettableFuture;
15 import javax.annotation.PreDestroy;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.opendaylight.netconf.api.TransportConstants;
19 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
20 import org.opendaylight.netconf.common.NetconfTimer;
21 import org.opendaylight.netconf.transport.api.TransportChannel;
22 import org.opendaylight.netconf.transport.api.TransportChannelListener;
23 import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
24 import org.opendaylight.netconf.transport.ssh.SSHTransportStackFactory;
25 import org.opendaylight.netconf.transport.tcp.TCPClient;
26 import org.opendaylight.netconf.transport.tls.FixedSslHandlerFactory;
27 import org.opendaylight.netconf.transport.tls.TLSClient;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
29 import org.osgi.service.component.annotations.Activate;
30 import org.osgi.service.component.annotations.Component;
31 import org.osgi.service.component.annotations.Deactivate;
32 import org.osgi.service.component.annotations.Reference;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 @Singleton
37 @Component(service = NetconfClientFactory.class)
38 public final class NetconfClientFactoryImpl implements NetconfClientFactory {
39     private static final Logger LOG = LoggerFactory.getLogger(NetconfClientFactoryImpl.class);
40
41     private final SSHTransportStackFactory factory;
42     private final NetconfTimer timer;
43
44     public NetconfClientFactoryImpl(final NetconfTimer timer, final SSHTransportStackFactory factory) {
45         this.timer = requireNonNull(timer);
46         this.factory = requireNonNull(factory);
47     }
48
49     @Inject
50     @Activate
51     public NetconfClientFactoryImpl(@Reference final NetconfTimer timer) {
52         // FIXME: make factory component configurable for OSGi
53         this(timer, new SSHTransportStackFactory("odl-netconf-client", 0));
54     }
55
56     @PreDestroy
57     @Deactivate
58     @Override
59     public void close() {
60         factory.close();
61     }
62
63     @Override
64     public ListenableFuture<NetconfClientSession> createClient(final NetconfClientConfiguration configuration)
65             throws UnsupportedConfigurationException {
66         final var future = SettableFuture.<NetconfClientSession>create();
67         final var channelInitializer = new ClientChannelInitializer(createNegotiatorFactory(configuration),
68             () -> configuration.getSessionListener());
69
70         // FIXME: do not ignore this future
71         final var stackFuture = switch (configuration.getProtocol()) {
72             case SSH -> factory.connectClient(TransportConstants.SSH_SUBSYSTEM,
73                 new ClientTransportChannelListener(future, channelInitializer), configuration.getTcpParameters(),
74                 configuration.getSshParameters(), configuration.getSshConfigurator());
75             case TCP -> TCPClient.connect(new ClientTransportChannelListener(future, channelInitializer),
76                 factory.newBootstrap(), configuration.getTcpParameters());
77             case TLS -> {
78                 var handlerFactory = configuration.getSslHandlerFactory();
79                 if (handlerFactory == null) {
80                     handlerFactory = new FixedSslHandlerFactory(configuration.getTlsParameters());
81                 }
82                 yield TLSClient.connect(new ClientTransportChannelListener(future, channelInitializer),
83                     factory.newBootstrap(), configuration.getTcpParameters(), handlerFactory);
84             }
85         };
86         LOG.trace("Future stack is {}", stackFuture);
87
88         return future;
89     }
90
91     private NetconfClientSessionNegotiatorFactory createNegotiatorFactory(
92             final NetconfClientConfiguration configuration) {
93         final var capabilities = configuration.getOdlHelloCapabilities();
94         if (capabilities == null || capabilities.isEmpty()) {
95             return new NetconfClientSessionNegotiatorFactory(timer, configuration.getAdditionalHeader(),
96                 configuration.getConnectionTimeoutMillis(), configuration.getMaximumIncomingChunkSize());
97         }
98         final var stringCapabilities = capabilities.stream().map(Uri::getValue)
99             .collect(ImmutableSet.toImmutableSet());
100         return new NetconfClientSessionNegotiatorFactory(timer, configuration.getAdditionalHeader(),
101             configuration.getConnectionTimeoutMillis(), stringCapabilities);
102     }
103
104     private record ClientTransportChannelListener(
105             SettableFuture<NetconfClientSession> future,
106             ClientChannelInitializer initializer) implements TransportChannelListener {
107         ClientTransportChannelListener {
108             requireNonNull(future);
109             requireNonNull(initializer);
110         }
111
112         @Override
113         public void onTransportChannelEstablished(final TransportChannel channel) {
114             final var nettyChannel = channel.channel();
115             final var promise = nettyChannel.eventLoop().<NetconfClientSession>newPromise();
116             initializer.initialize(nettyChannel, promise);
117             promise.addListener(ignored -> {
118                 final var cause = promise.cause();
119                 if (cause != null) {
120                     future.setException(cause);
121                 } else {
122                     future.set(promise.getNow());
123                 }
124             });
125         }
126
127         @Override
128         public void onTransportChannelFailed(final Throwable cause) {
129             future.setException(cause);
130         }
131     }
132 }