BUG-848 Fix netconf communication while using CHUNK encoding
[controller.git] / opendaylight / netconf / netconf-client / src / main / java / org / opendaylight / controller / netconf / client / NetconfClientSessionNegotiator.java
index f8f73fc8e5296b9534e0759bc43f24bbf493521e..0c5935b57162b44354a0db08a734886d18bd386a 100644 (file)
@@ -8,13 +8,14 @@
 
 package org.opendaylight.controller.netconf.client;
 
-import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
+import java.util.Collection;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+
 import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
@@ -22,6 +23,7 @@ import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
 import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
 import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
+import org.opendaylight.controller.netconf.util.messages.NetconfStartExiMessage;
 import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -31,8 +33,9 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
 
 public class NetconfClientSessionNegotiator extends
         AbstractNetconfSessionNegotiator<NetconfClientSessionPreferences, NetconfClientSession, NetconfClientSessionListener>
@@ -55,19 +58,45 @@ public class NetconfClientSessionNegotiator extends
 
     @Override
     protected void handleMessage(NetconfHelloMessage netconfMessage) throws NetconfDocumentedException {
-        NetconfClientSession session = super.getSessionForHelloMessage(netconfMessage);
-
-        if (shouldUseExi(netconfMessage.getDocument())){
-            logger.debug("Netconf session: {} should use exi.", session);
-            tryToStartExi(session);
+        final NetconfClientSession session = getSessionForHelloMessage(netconfMessage);
+        replaceHelloMessageInboundHandler(session);
+
+        // If exi should be used, try to initiate exi communication
+        // Call negotiationSuccessFul after exi negotiation is finished successfully or not
+        if (shouldUseExi(netconfMessage)) {
+            logger.debug("Netconf session {} should use exi.", session);
+            NetconfStartExiMessage startExiMessage = (NetconfStartExiMessage) sessionPreferences.getStartExiMessage();
+            tryToInitiateExi(session, startExiMessage);
+        // Exi is not supported, release session immediately
         } else {
             logger.debug("Netconf session {} isn't capable using exi.", session);
             negotiationSuccessful(session);
         }
     }
 
-    private boolean shouldUseExi(Document doc) {
-        return containsExi10Capability(doc)
+    /**
+     * Initiates exi communication by sending start-exi message and waiting for positive/negative response.
+     *
+     * @param startExiMessage
+     */
+    void tryToInitiateExi(final NetconfClientSession session, final NetconfStartExiMessage startExiMessage) {
+        session.sendMessage(startExiMessage).addListener(new ChannelFutureListener() {
+            @Override
+            public void operationComplete(final ChannelFuture f) {
+                if (!f.isSuccess()) {
+                    logger.warn("Failed to send start-exi message {} on session {}", startExiMessage, this, f.cause());
+                } else {
+                    logger.trace("Start-exi message {} sent to socket on session {}", startExiMessage, this);
+                    channel.pipeline().addAfter(
+                            AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER,
+                            new ExiConfirmationInboundHandler(session, startExiMessage));
+                }
+            }
+        });
+    }
+
+    private boolean shouldUseExi(NetconfHelloMessage helloMsg) {
+        return containsExi10Capability(helloMsg.getDocument())
                 && containsExi10Capability(sessionPreferences.getHelloMessage().getDocument());
     }
 
@@ -81,23 +110,6 @@ public class NetconfClientSessionNegotiator extends
         return false;
     }
 
-    private void tryToStartExi(final NetconfClientSession session) {
-        final NetconfMessage startExi = sessionPreferences.getStartExiMessage();
-        session.sendMessage(startExi).addListener(new ChannelFutureListener() {
-            @Override
-            public void operationComplete(final ChannelFuture f) {
-                if (!f.isSuccess()) {
-                    logger.warn("Failed to send start-exi message {} on session {}", startExi, session, f.cause());
-                } else {
-                    logger.trace("Start-exi message {} sent to socket on session {}", startExi, session);
-                    NetconfClientSessionNegotiator.this.channel.pipeline().addAfter(
-                            AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER,
-                            new ExiConfirmationInboundHandler(session));
-                }
-            }
-        });
-    }
-
     private long extractSessionId(Document doc) {
         final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE);
         String textContent = sessionIdNode.getTextContent();
@@ -109,9 +121,11 @@ public class NetconfClientSessionNegotiator extends
     }
 
     @Override
-    protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel, NetconfHelloMessage message) throws NetconfDocumentedException {
-        return new NetconfClientSession(sessionListener, channel, extractSessionId(message.getDocument()),
-                NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument()));
+    protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel,
+            NetconfHelloMessage message) throws NetconfDocumentedException {
+        long sessionId = extractSessionId(message.getDocument());
+        Collection<String> capabilities = NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument());
+        return new NetconfClientSession(sessionListener, channel, sessionId, capabilities);
     }
 
     /**
@@ -121,9 +135,11 @@ public class NetconfClientSessionNegotiator extends
         private static final String EXI_CONFIRMED_HANDLER = "exiConfirmedHandler";
 
         private final NetconfClientSession session;
+        private NetconfStartExiMessage startExiMessage;
 
-        ExiConfirmationInboundHandler(NetconfClientSession session) {
+        ExiConfirmationInboundHandler(NetconfClientSession session, final NetconfStartExiMessage startExiMessage) {
             this.session = session;
+            this.startExiMessage = startExiMessage;
         }
 
         @Override
@@ -136,19 +152,19 @@ public class NetconfClientSessionNegotiator extends
             if (NetconfMessageUtil.isOKMessage(netconfMessage)) {
                 logger.trace("Positive response on start-exi call received on session {}", session);
                 try {
-                    session.startExiCommunication(sessionPreferences.getStartExiMessage());
+                    session.startExiCommunication(startExiMessage);
                 } catch (RuntimeException e) {
                     // Unable to add exi, continue without exi
                     logger.warn("Unable to start exi communication, Communication will continue without exi on session {}", session, e);
                 }
 
-            // Error response
+                // Error response
             } else if(NetconfMessageUtil.isErrorMessage(netconfMessage)) {
                 logger.warn(
                         "Error response to start-exi message {}, Communication will continue without exi on session {}",
                         XmlUtil.toString(netconfMessage.getDocument()), session);
 
-            // Unexpected response to start-exi, throwing message away, continue without exi
+                // Unexpected response to start-exi, throwing message away, continue without exi
             } else {
                 logger.warn(
                         "Unexpected response to start-exi message, should be ok, was {}, " +
@@ -159,4 +175,5 @@ public class NetconfClientSessionNegotiator extends
             negotiationSuccessful(session);
         }
     }
+
 }