2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.controller.netconf.client;
11 import javax.xml.xpath.XPathConstants;
12 import javax.xml.xpath.XPathExpression;
14 import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences;
15 import org.opendaylight.controller.netconf.api.NetconfMessage;
16 import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
17 import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
18 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
19 import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
20 import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
21 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
22 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import org.w3c.dom.Document;
26 import org.w3c.dom.Node;
27 import org.w3c.dom.NodeList;
29 import io.netty.channel.Channel;
30 import io.netty.channel.ChannelFuture;
31 import io.netty.channel.ChannelFutureListener;
32 import io.netty.channel.ChannelHandlerContext;
33 import io.netty.channel.ChannelInboundHandlerAdapter;
34 import io.netty.util.Timer;
35 import io.netty.util.concurrent.Promise;
37 public class NetconfClientSessionNegotiator extends
38 AbstractNetconfSessionNegotiator<NetconfClientSessionPreferences, NetconfClientSession, NetconfClientSessionListener>
40 private static final Logger logger = LoggerFactory.getLogger(NetconfClientSessionNegotiator.class);
42 private static final XPathExpression sessionIdXPath = XMLNetconfUtil
43 .compileXPath("/netconf:hello/netconf:session-id");
45 private static final String EXI_1_0_CAPABILITY_MARKER = "exi:1.0";
47 protected NetconfClientSessionNegotiator(NetconfClientSessionPreferences sessionPreferences,
48 Promise<NetconfClientSession> promise,
51 NetconfClientSessionListener sessionListener,
52 long connectionTimeoutMillis) {
53 super(sessionPreferences, promise, channel, timer, sessionListener, connectionTimeoutMillis);
57 protected void handleMessage(NetconfHelloMessage netconfMessage) {
58 NetconfClientSession session = super.getSessionForHelloMessage(netconfMessage);
60 if (shouldUseExi(netconfMessage.getDocument())){
61 logger.info("Netconf session: {} should use exi.", session);
62 tryToStartExi(session);
64 logger.info("Netconf session {} isn't capable using exi.", session);
65 negotiationSuccessful(session);
69 private boolean shouldUseExi(Document doc) {
70 return containsExi10Capability(doc)
71 && containsExi10Capability(sessionPreferences.getHelloMessage().getDocument());
74 private boolean containsExi10Capability(final Document doc) {
75 final NodeList nList = doc.getElementsByTagName(XmlNetconfConstants.CAPABILITY);
76 for (int i = 0; i < nList.getLength(); i++) {
77 if (nList.item(i).getTextContent().contains(EXI_1_0_CAPABILITY_MARKER)) {
84 private void tryToStartExi(final NetconfClientSession session) {
85 final NetconfMessage startExi = sessionPreferences.getStartExiMessage();
86 session.sendMessage(startExi).addListener(new ChannelFutureListener() {
88 public void operationComplete(final ChannelFuture f) {
90 logger.warn("Failed to send start-exi message {} on session {}", startExi, session, f.cause());
92 logger.trace("Start-exi message {} sent to socket on session {}", startExi, session);
93 NetconfClientSessionNegotiator.this.channel.pipeline().addAfter(
94 AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER,
95 new ExiConfirmationInboundHandler(session));
101 private long extractSessionId(Document doc) {
102 final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE);
103 String textContent = sessionIdNode.getTextContent();
104 if (textContent == null || textContent.equals("")) {
105 throw new IllegalStateException("Session id not received from server");
108 return Long.valueOf(textContent);
112 protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel, NetconfHelloMessage message) {
113 return new NetconfClientSession(sessionListener, channel, extractSessionId(message.getDocument()),
114 NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument()));
118 * Handler to process response for start-exi message
120 private final class ExiConfirmationInboundHandler extends ChannelInboundHandlerAdapter {
121 private static final String EXI_CONFIRMED_HANDLER = "exiConfirmedHandler";
123 private final NetconfClientSession session;
125 ExiConfirmationInboundHandler(NetconfClientSession session) {
126 this.session = session;
130 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
131 ctx.pipeline().remove(ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER);
133 NetconfMessage netconfMessage = (NetconfMessage) msg;
135 // Ok response to start-exi, try to add exi handlers
136 if (NetconfMessageUtil.isOKMessage(netconfMessage)) {
137 logger.trace("Positive response on start-exi call received on session {}", session);
139 session.startExiCommunication(sessionPreferences.getStartExiMessage());
140 } catch (RuntimeException e) {
141 // Unable to add exi, continue without exi
142 logger.warn("Unable to start exi communication, Communication will continue without exi on session {}", session, e);
146 } else if(NetconfMessageUtil.isErrorMessage(netconfMessage)) {
148 "Error response to start-exi message {}, Communication will continue without exi on session {}",
149 XmlUtil.toString(netconfMessage.getDocument()), session);
151 // Unexpected response to start-exi, throwing message away, continue without exi
154 "Unexpected response to start-exi message, should be ok, was {}, " +
155 "Communication will continue without exi and response message will be thrown away on session {}",
156 XmlUtil.toString(netconfMessage.getDocument()), session);
159 negotiationSuccessful(session);