*/
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;
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;
// 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
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);
@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 {
*
* @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));
});
}
- private boolean shouldUseExi(final NetconfHelloMessage helloMsg) {
+ private boolean shouldUseExi(final HelloMessage helloMsg) {
return containsExi10Capability(helloMsg.getDocument()) && containsExi10Capability(localHello().getDocument());
}
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);
.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.
*/
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;
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
// 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);