From 4eb7c36e28e610ee78b58e52f43af6c5527c9ebe Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Sun, 14 Nov 2021 15:38:59 +0100 Subject: [PATCH] Fix AbstractNetconfSessionNegotiator state access MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Eliminate anonymous class which was mistakenly used as a synchronization point. JIRA: NETCONF-827 Change-Id: I3f81c7bda5f08bfc421859c2b0960beca5269c9a Signed-off-by: Jaroslav Tóth Signed-off-by: Robert Varga --- .../AbstractNetconfSessionNegotiator.java | 84 +++++++++---------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiator.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiator.java index 171d8d2cdf..63c6668a1a 100644 --- a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiator.java +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiator.java @@ -19,11 +19,11 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.ssl.SslHandler; import io.netty.util.Timeout; import io.netty.util.Timer; -import io.netty.util.TimerTask; import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.Promise; import java.util.Optional; import java.util.concurrent.TimeUnit; +import org.checkerframework.checker.lock.qual.GuardedBy; import org.opendaylight.netconf.api.NetconfDocumentedException; import org.opendaylight.netconf.api.NetconfMessage; import org.opendaylight.netconf.api.NetconfSessionListener; @@ -44,6 +44,12 @@ import org.w3c.dom.NodeList; public abstract class AbstractNetconfSessionNegotiator

, L extends NetconfSessionListener> extends ChannelInboundHandlerAdapter implements NetconfSessionNegotiator { + /** + * Possible states for Finite State Machine. + */ + protected enum State { + IDLE, OPEN_WAIT, FAILED, ESTABLISHED + } private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfSessionNegotiator.class); @@ -52,20 +58,15 @@ public abstract class AbstractNetconfSessionNegotiator

promise; private final L sessionListener; - private Timeout timeout; + private final Timer timer; - /** - * Possible states for Finite State Machine. - */ - protected enum State { - IDLE, OPEN_WAIT, FAILED, ESTABLISHED - } + private Timeout timeoutTask; + @GuardedBy("this") private State state = State.IDLE; - private final Timer timer; - private final long connectionTimeoutMillis; protected AbstractNetconfSessionNegotiator(final P sessionPreferences, final Promise promise, final Channel channel, final Timer timer, @@ -119,49 +120,39 @@ public abstract class AbstractNetconfSessionNegotiator

) future -> { - if (future.isSuccess()) { - LOG.debug("Channel {} closed: success", future.channel()); - } else { - LOG.warn("Channel {} closed: fail", future.channel()); - } - }); - } - } else if (channel.isOpen()) { - channel.pipeline().remove(NAME_OF_EXCEPTION_HANDLER); - } - } - } + timeoutTask = this.timer.newTimeout(this::timeoutExpired, connectionTimeoutMillis, TimeUnit.MILLISECONDS); + } - private boolean isPromiseFinished() { - return promise.isDone() || promise.isCancelled(); + private synchronized void timeoutExpired(final Timeout timeout) { + if (state != State.ESTABLISHED) { + LOG.debug("Connection timeout after {}, session backed by channel {} is in state {}", timeout, channel, + state); + + // Do not fail negotiation if promise is done or canceled + // It would result in setting result of the promise second time and that throws exception + if (!promise.isDone() && !promise.isCancelled()) { + LOG.warn("Netconf session backed by channel {} was not established after {}", channel, + connectionTimeoutMillis); + changeState(State.FAILED); + + channel.close().addListener((GenericFutureListener) future -> { + if (future.isSuccess()) { + LOG.debug("Channel {} closed: success", future.channel()); + } else { + LOG.warn("Channel {} closed: fail", future.channel()); + } + }); } - - }, connectionTimeoutMillis, TimeUnit.MILLISECONDS); + } else if (channel.isOpen()) { + channel.pipeline().remove(NAME_OF_EXCEPTION_HANDLER); + } } @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "https://github.com/spotbugs/spotbugs/issues/811") private void cancelTimeout() { - if (timeout != null) { - timeout.cancel(); + if (timeoutTask != null) { + timeoutTask.cancel(); } } @@ -275,6 +266,7 @@ public abstract class AbstractNetconfSessionNegotiator