import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageAggregator;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageChunkDecoder;
+import org.opendaylight.controller.netconf.util.messages.FramingMechanism;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
import java.util.concurrent.TimeUnit;
public abstract class AbstractNetconfSessionNegotiator<P extends NetconfSessionPreferences, S extends NetconfSession>
extends AbstractSessionNegotiator<NetconfMessage, S> {
- // TODO what time ?
+ // TODO Adjust wait time for negotiation, now is 1 minute ?
private static final long INITIAL_HOLDTIMER = 1;
+
private static final Logger logger = LoggerFactory.getLogger(AbstractNetconfSessionNegotiator.class);
+ public static final String NAME_OF_EXCEPTION_HANDLER = "lastExceptionHandler";
protected final P sessionPreferences;
private final SessionListener sessionListener;
+ private Timeout timeout;
/**
* Possible states for Finite State Machine
}
@Override
- protected void startNegotiation() throws Exception {
+ protected void startNegotiation() {
final Optional<SslHandler> sslHandler = getSslHandler(channel);
if (sslHandler.isPresent()) {
Future<Channel> future = sslHandler.get().handshakeFuture();
final NetconfMessage helloMessage = this.sessionPreferences.getHelloMessage();
logger.debug("Session negotiation started with hello message {}", XmlUtil.toString(helloMessage.getDocument()));
- sendMessage(helloMessage);
- changeState(State.OPEN_WAIT);
+ channel.pipeline().addLast(NAME_OF_EXCEPTION_HANDLER, new ChannelHandler() {
+ @Override
+ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ logger.warn("An exception occurred during negotiation on channel {}", channel.localAddress(), cause);
+ cancelTimeout();
+ negotiationFailed(cause);
+ changeState(State.FAILED);
+ }
+ });
- this.timer.newTimeout(new TimerTask() {
+ timeout = this.timer.newTimeout(new TimerTask() {
@Override
public void run(final Timeout timeout) throws Exception {
synchronized (this) {
"Session was not established after " + timeout);
negotiationFailed(cause);
changeState(State.FAILED);
+ } else if(channel.isOpen()) {
+ channel.pipeline().remove(NAME_OF_EXCEPTION_HANDLER);
}
}
}
}, INITIAL_HOLDTIMER, TimeUnit.MINUTES);
+
+ sendMessage(helloMessage);
+ changeState(State.OPEN_WAIT);
+ }
+
+ private void cancelTimeout() {
+ if(timeout!=null)
+ timeout.cancel();
}
private void sendMessage(NetconfMessage message) {
final Document doc = netconfMessage.getDocument();
if (isHelloMessage(doc)) {
+ if (containsBase11Capability(doc)
+ && containsBase11Capability(sessionPreferences.getHelloMessage().getDocument())) {
+ channel.pipeline().replace("frameEncoder", "frameEncoder",
+ FramingMechanismHandlerFactory.createHandler(FramingMechanism.CHUNK));
+ channel.pipeline().replace("aggregator", "aggregator",
+ new NetconfMessageAggregator(FramingMechanism.CHUNK));
+ channel.pipeline().addAfter("aggregator", "chunkDecoder", new NetconfMessageChunkDecoder());
+ }
changeState(State.ESTABLISHED);
- S session = getSession(sessionListener, channel, doc);
+ S session = getSession(sessionListener, channel, netconfMessage);
negotiationSuccessful(session);
} else {
final IllegalStateException cause = new IllegalStateException(
"Received message was not hello as expected, but was " + XmlUtil.toString(doc));
+ logger.warn("Negotiation of netconf session failed", cause);
negotiationFailed(cause);
}
}
- protected abstract S getSession(SessionListener sessionListener, Channel channel, Document doc);
+ protected abstract S getSession(SessionListener sessionListener, Channel channel, NetconfMessage message);
private boolean isHelloMessage(Document doc) {
try {
this.state = newState;
}
+ private boolean containsBase11Capability(final Document doc) {
+ final NodeList nList = doc.getElementsByTagName("capability");
+ for (int i = 0; i < nList.getLength(); i++) {
+ if (nList.item(i).getTextContent().contains("base:1.1")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static boolean isStateChangePermitted(State state, State newState) {
if (state == State.IDLE && newState == State.OPEN_WAIT)
return true;