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 io.netty.channel.Channel;
12 import io.netty.channel.ChannelFuture;
13 import io.netty.channel.ChannelFutureListener;
14 import io.netty.channel.ChannelHandlerContext;
15 import io.netty.channel.ChannelInboundHandlerAdapter;
16 import io.netty.util.Timer;
17 import io.netty.util.concurrent.Promise;
19 import java.util.Collection;
21 import javax.xml.xpath.XPathConstants;
22 import javax.xml.xpath.XPathExpression;
24 import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences;
25 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
26 import org.opendaylight.controller.netconf.api.NetconfMessage;
27 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
28 import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer;
29 import org.opendaylight.controller.netconf.nettyutil.AbstractNetconfSessionNegotiator;
30 import org.opendaylight.controller.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
31 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
32 import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
33 import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
34 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.w3c.dom.Document;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.NodeList;
41 public class NetconfClientSessionNegotiator extends
42 AbstractNetconfSessionNegotiator<NetconfClientSessionPreferences, NetconfClientSession, NetconfClientSessionListener>
44 private static final Logger logger = LoggerFactory.getLogger(NetconfClientSessionNegotiator.class);
46 private static final XPathExpression sessionIdXPath = XMLNetconfUtil
47 .compileXPath("/netconf:hello/netconf:session-id");
49 private static final String EXI_1_0_CAPABILITY_MARKER = "exi:1.0";
51 protected NetconfClientSessionNegotiator(NetconfClientSessionPreferences sessionPreferences,
52 Promise<NetconfClientSession> promise,
55 NetconfClientSessionListener sessionListener,
56 long connectionTimeoutMillis) {
57 super(sessionPreferences, promise, channel, timer, sessionListener, connectionTimeoutMillis);
61 protected void handleMessage(NetconfHelloMessage netconfMessage) throws NetconfDocumentedException {
62 final NetconfClientSession session = getSessionForHelloMessage(netconfMessage);
63 replaceHelloMessageInboundHandler(session);
65 // If exi should be used, try to initiate exi communication
66 // Call negotiationSuccessFul after exi negotiation is finished successfully or not
67 if (shouldUseExi(netconfMessage)) {
68 logger.debug("Netconf session {} should use exi.", session);
69 NetconfStartExiMessage startExiMessage = (NetconfStartExiMessage) sessionPreferences.getStartExiMessage();
70 tryToInitiateExi(session, startExiMessage);
71 // Exi is not supported, release session immediately
73 logger.debug("Netconf session {} isn't capable of using exi.", session);
74 negotiationSuccessful(session);
79 * Initiates exi communication by sending start-exi message and waiting for positive/negative response.
81 * @param startExiMessage
83 void tryToInitiateExi(final NetconfClientSession session, final NetconfStartExiMessage startExiMessage) {
84 channel.pipeline().addAfter(AbstractChannelInitializer.NETCONF_MESSAGE_DECODER,
85 ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER,
86 new ExiConfirmationInboundHandler(session, startExiMessage));
88 session.sendMessage(startExiMessage).addListener(new ChannelFutureListener() {
90 public void operationComplete(final ChannelFuture f) {
92 logger.warn("Failed to send start-exi message {} on session {}", startExiMessage, this, f.cause());
93 channel.pipeline().remove(ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER);
95 logger.trace("Start-exi message {} sent to socket on session {}", startExiMessage, this);
101 private boolean shouldUseExi(NetconfHelloMessage helloMsg) {
102 return containsExi10Capability(helloMsg.getDocument())
103 && containsExi10Capability(sessionPreferences.getHelloMessage().getDocument());
106 private boolean containsExi10Capability(final Document doc) {
107 final NodeList nList = doc.getElementsByTagName(XmlNetconfConstants.CAPABILITY);
108 for (int i = 0; i < nList.getLength(); i++) {
109 if (nList.item(i).getTextContent().contains(EXI_1_0_CAPABILITY_MARKER)) {
116 private long extractSessionId(Document doc) {
117 final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE);
118 String textContent = sessionIdNode.getTextContent();
119 if (textContent == null || textContent.equals("")) {
120 throw new IllegalStateException("Session id not received from server");
123 return Long.valueOf(textContent);
127 protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel,
128 NetconfHelloMessage message) throws NetconfDocumentedException {
129 long sessionId = extractSessionId(message.getDocument());
130 Collection<String> capabilities = NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument());
131 return new NetconfClientSession(sessionListener, channel, sessionId, capabilities);
135 * Handler to process response for start-exi message
137 private final class ExiConfirmationInboundHandler extends ChannelInboundHandlerAdapter {
138 private static final String EXI_CONFIRMED_HANDLER = "exiConfirmedHandler";
140 private final NetconfClientSession session;
141 private NetconfStartExiMessage startExiMessage;
143 ExiConfirmationInboundHandler(NetconfClientSession session, final NetconfStartExiMessage startExiMessage) {
144 this.session = session;
145 this.startExiMessage = startExiMessage;
149 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
150 ctx.pipeline().remove(ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER);
152 NetconfMessage netconfMessage = (NetconfMessage) msg;
154 // Ok response to start-exi, try to add exi handlers
155 if (NetconfMessageUtil.isOKMessage(netconfMessage)) {
156 logger.trace("Positive response on start-exi call received on session {}", session);
158 session.startExiCommunication(startExiMessage);
159 } catch (RuntimeException e) {
160 // Unable to add exi, continue without exi
161 logger.warn("Unable to start exi communication, Communication will continue without exi on session {}", session, e);
165 } else if(NetconfMessageUtil.isErrorMessage(netconfMessage)) {
167 "Error response to start-exi message {}, Communication will continue without exi on session {}",
168 XmlUtil.toString(netconfMessage.getDocument()), session);
170 // Unexpected response to start-exi, throwing message away, continue without exi
173 "Unexpected response to start-exi message, should be ok, was {}, " +
174 "Communication will continue without exi and response message will be thrown away on session {}",
175 XmlUtil.toString(netconfMessage.getDocument()), session);
178 negotiationSuccessful(session);