Introduce NetconfTimer
[netconf.git] / protocol / netconf-client / src / main / java / org / opendaylight / netconf / client / NetconfClientSessionNegotiator.java
index 0c98fa3f42edf08fbc86c90e7b515c514fe9ce6e..8455821ef5aca833b14fff2dfef0e6ac52589f4d 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.netconf.client;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Interner;
@@ -14,22 +15,24 @@ import com.google.common.collect.Interners;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.util.Timer;
 import io.netty.util.concurrent.Promise;
 import java.util.Set;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
 import org.checkerframework.checker.index.qual.NonNegative;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
-import org.opendaylight.netconf.api.NetconfMessage;
-import org.opendaylight.netconf.api.messages.NetconfHelloMessage;
+import org.opendaylight.netconf.api.messages.HelloMessage;
+import org.opendaylight.netconf.api.messages.NetconfMessage;
+import org.opendaylight.netconf.api.messages.RpcMessage;
 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.netconf.api.xml.XmlUtil;
+import org.opendaylight.netconf.common.NetconfTimer;
 import org.opendaylight.netconf.nettyutil.AbstractChannelInitializer;
-import org.opendaylight.netconf.nettyutil.AbstractNetconfSessionNegotiator;
-import org.opendaylight.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
-import org.opendaylight.netconf.util.messages.NetconfMessageUtil;
-import org.opendaylight.netconf.util.xml.XMLNetconfUtil;
+import org.opendaylight.netconf.nettyutil.NetconfSessionNegotiator;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.SessionIdType;
+import org.opendaylight.yangtools.yang.common.Uint32;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -38,7 +41,7 @@ import org.w3c.dom.NodeList;
 
 // Non-final for mocking
 class NetconfClientSessionNegotiator
-        extends AbstractNetconfSessionNegotiator<NetconfClientSession, NetconfClientSessionListener> {
+        extends NetconfSessionNegotiator<NetconfClientSession, NetconfClientSessionListener> {
     private static final Logger LOG = LoggerFactory.getLogger(NetconfClientSessionNegotiator.class);
 
     private static final XPathExpression SESSION_ID_X_PATH = XMLNetconfUtil
@@ -51,10 +54,10 @@ class NetconfClientSessionNegotiator
 
     private static final Interner<Set<String>> INTERNER = Interners.newWeakInterner();
 
-    private final NetconfStartExiMessage startExi;
+    private final RpcMessage startExi;
 
-    NetconfClientSessionNegotiator(final NetconfHelloMessage hello, final NetconfStartExiMessage startExi,
-            final Promise<NetconfClientSession> promise, final Channel channel, final Timer timer,
+    NetconfClientSessionNegotiator(final HelloMessage hello, final RpcMessage startExi,
+            final Promise<NetconfClientSession> promise, final Channel channel, final NetconfTimer timer,
             final NetconfClientSessionListener sessionListener, final long connectionTimeoutMillis,
             final @NonNegative int maximumIncomingChunkSize) {
         super(hello, promise, channel, timer, sessionListener, connectionTimeoutMillis, maximumIncomingChunkSize);
@@ -63,7 +66,7 @@ class NetconfClientSessionNegotiator
 
     @SuppressWarnings("checkstyle:IllegalCatch")
     @Override
-    protected void handleMessage(final NetconfHelloMessage netconfMessage) throws NetconfDocumentedException {
+    protected void handleMessage(final HelloMessage netconfMessage) throws NetconfDocumentedException {
         if (!ifNegotiatedAlready()) {
             LOG.debug("Server hello message received, starting negotiation on channel {}", channel);
             try {
@@ -94,7 +97,7 @@ class NetconfClientSessionNegotiator
      *
      * @param startExiMessage Exi message for initilization of exi communication.
      */
-    void tryToInitiateExi(final NetconfClientSession session, final NetconfStartExiMessage startExiMessage) {
+    void tryToInitiateExi(final NetconfClientSession session, final RpcMessage startExiMessage) {
         channel.pipeline().addAfter(AbstractChannelInitializer.NETCONF_MESSAGE_DECODER,
                 ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER,
                 new ExiConfirmationInboundHandler(session, startExiMessage));
@@ -110,7 +113,7 @@ class NetconfClientSessionNegotiator
         });
     }
 
-    private boolean shouldUseExi(final NetconfHelloMessage helloMsg) {
+    private boolean shouldUseExi(final HelloMessage helloMsg) {
         return containsExi10Capability(helloMsg.getDocument()) && containsExi10Capability(localHello().getDocument());
     }
 
@@ -124,7 +127,7 @@ class NetconfClientSessionNegotiator
         return false;
     }
 
-    private static long extractSessionId(final Document doc) {
+    private static @NonNull SessionIdType extractSessionId(final Document doc) {
         String textContent = getSessionIdWithXPath(doc, SESSION_ID_X_PATH);
         if (Strings.isNullOrEmpty(textContent)) {
             textContent = getSessionIdWithXPath(doc, SESSION_ID_X_PATH_NO_NAMESPACE);
@@ -133,29 +136,35 @@ class NetconfClientSessionNegotiator
                         .toString(doc));
             }
         }
-
-        return Long.parseLong(textContent);
+        return new SessionIdType(Uint32.valueOf(textContent));
     }
 
     private static String getSessionIdWithXPath(final Document doc, final XPathExpression sessionIdXPath) {
-        final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE);
+        final var sessionIdNode = evaluateXPath(sessionIdXPath, doc);
         return sessionIdNode != null ? sessionIdNode.getTextContent() : null;
     }
 
     @Override
     protected NetconfClientSession getSession(final NetconfClientSessionListener sessionListener, final Channel channel,
-                                              final NetconfHelloMessage message) {
-        final long sessionId = extractSessionId(message.getDocument());
+                                              final HelloMessage message) {
+        final var sessionId = extractSessionId(message.getDocument());
 
         // Copy here is important: it disconnects the strings from the document
-        Set<String> capabilities = ImmutableSet.copyOf(NetconfMessageUtil.extractCapabilitiesFromHello(message
-                .getDocument()));
-
-        capabilities = INTERNER.intern(capabilities);
+        final var capabilities = INTERNER.intern(ImmutableSet.copyOf(
+            NetconfMessageUtil.extractCapabilitiesFromHello(message .getDocument())));
 
         return new NetconfClientSession(sessionListener, channel, sessionId, capabilities);
     }
 
+    @VisibleForTesting
+    static Node evaluateXPath(final XPathExpression expr, final Object rootNode) {
+        try {
+            return (Node) expr.evaluate(rootNode, XPathConstants.NODE);
+        } catch (final XPathExpressionException e) {
+            throw new IllegalStateException("Error while evaluating xpath expression " + expr, e);
+        }
+    }
+
     /**
      * Handler to process response for start-exi message.
      */
@@ -163,17 +172,17 @@ class NetconfClientSessionNegotiator
         private static final String EXI_CONFIRMED_HANDLER = "exiConfirmedHandler";
 
         private final NetconfClientSession session;
-        private final NetconfStartExiMessage startExiMessage;
+        private final RpcMessage startExiMessage;
 
         ExiConfirmationInboundHandler(final NetconfClientSession session,
-                                      final NetconfStartExiMessage startExiMessage) {
+                                      final RpcMessage startExiMessage) {
             this.session = session;
             this.startExiMessage = startExiMessage;
         }
 
         @SuppressWarnings("checkstyle:IllegalCatch")
         @Override
-        public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
+        public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
             ctx.pipeline().remove(ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER);
 
             NetconfMessage netconfMessage = (NetconfMessage) msg;
@@ -185,8 +194,8 @@ class NetconfClientSessionNegotiator
                     session.startExiCommunication(startExiMessage);
                 } catch (RuntimeException e) {
                     // Unable to add exi, continue without exi
-                    LOG.warn("Unable to start exi communication, Communication will continue without exi on session "
-                            + "{}", session, e);
+                    LOG.warn("Unable to start exi communication, Communication will continue without exi on session {}",
+                        session, e);
                 }
 
                 // Error response
@@ -197,10 +206,9 @@ class NetconfClientSessionNegotiator
 
                 // Unexpected response to start-exi, throwing message away, continue without exi
             } else {
-                LOG.warn("Unexpected response to start-exi message, should be ok, was {}, "
-                        + "Communication will continue without exi "
-                        + "and response message will be thrown away on session {}",
-                        netconfMessage, session);
+                LOG.warn("""
+                    Unexpected response to start-exi message, should be ok, was {}. Communication will continue \
+                    without EXI and response message will be thrown away on session {}""", netconfMessage, session);
             }
 
             negotiationSuccessful(session);