1ea4287e493b8b60e46a7dbcf4372d15762bdfdc
[netconf.git] / transport / transport-ssh / src / main / java / org / opendaylight / netconf / transport / ssh / TransportServerSession.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.transport.ssh;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.util.concurrent.ListenableFuture;
13 import com.google.common.util.concurrent.SettableFuture;
14 import io.netty.channel.ChannelHandlerContext;
15 import java.io.IOException;
16 import java.lang.invoke.MethodHandles;
17 import java.lang.invoke.VarHandle;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.netconf.shaded.sshd.common.io.IoSession;
20 import org.opendaylight.netconf.shaded.sshd.server.session.ServerSessionImpl;
21 import org.opendaylight.netconf.transport.api.TransportChannel;
22
23 /**
24  * A {@link ServerSessionImpl}, bound to a backend Netty channel.
25  */
26 final class TransportServerSession extends ServerSessionImpl {
27     private record State(String subsystem, TransportChannel underlay, SettableFuture<ChannelHandlerContext> future) {
28         State {
29             subsystem = requireNonNull(subsystem);
30             underlay = requireNonNull(underlay);
31             future = requireNonNull(future);
32         }
33     }
34
35     private static final VarHandle STATE;
36
37     static {
38         try {
39             STATE = MethodHandles.lookup().findVarHandle(TransportServerSession.class, "state", State.class);
40         } catch (NoSuchFieldException | IllegalAccessException e) {
41             throw new ExceptionInInitializerError(e);
42         }
43     }
44
45     @SuppressWarnings("unused")
46     private volatile State state;
47
48     TransportServerSession(final TransportSshServer server, final IoSession ioSession) throws Exception {
49         super(server, ioSession);
50     }
51
52     ListenableFuture<ChannelHandlerContext> attachUnderlay(final String subsystem, final TransportChannel underlay) {
53         final var newState = new State(subsystem, underlay, SettableFuture.create());
54         final var witness = STATE.compareAndExchange(this, null, newState);
55         if (witness != null) {
56             throw new IllegalStateException("Already set up for " + witness);
57         }
58         return newState.future;
59     }
60
61     @Nullable TransportServerSubsystem openSubsystem(final String subsystem) {
62         final var local = (State) STATE.getAndSet(this, null);
63         if (local != null) {
64             if (subsystem.equals(local.subsystem)) {
65                 return new TransportServerSubsystem(subsystem, local.underlay, local.future);
66             }
67             local.future.setException(new IOException("Mismatched subsystem " + subsystem));
68         }
69         return null;
70     }
71 }