Refactor Additional header for netconf hello message. 63/5263/3
authorMaros Marsalek <mmarsale@cisco.com>
Tue, 11 Feb 2014 17:19:42 +0000 (18:19 +0100)
committerMaros Marsalek <mmarsale@cisco.com>
Thu, 13 Feb 2014 11:02:09 +0000 (12:02 +0100)
Allow additional header only for hello messages.
Introduce customized (en|decoders) for hello message to parse/serialize additional header.
These handlers are replaced after successful negotiation.

Change-Id: I54c441bc166e7141f503888f0eb1934882971045
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
23 files changed:
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/AdditionalHeaderUtil.java [deleted file]
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractChannelInitializer.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfHelloMessageToXMLEncoder.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfMessageToXMLEncoder.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfXMLToHelloMessageDecoder.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfXMLToMessageDecoder.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessageAdditionalHeader.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageAdditionalHeader.java [deleted file]
opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactoryTest.java

index 1d48e92..460aec6 100644 (file)
@@ -8,8 +8,6 @@
 
 package org.opendaylight.controller.netconf.persist.impl;
 
-import io.netty.channel.EventLoopGroup;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.InetSocketAddress;
@@ -30,7 +28,7 @@ import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.client.NetconfClient;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.netconf.util.NetconfUtil;
-import org.opendaylight.controller.netconf.util.messages.NetconfMessageAdditionalHeader;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -40,12 +38,12 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.xml.sax.SAXException;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import io.netty.channel.EventLoopGroup;
 
 @Immutable
 public class ConfigPusher {
-    private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
+    private static final Logger logger = LoggerFactory.getLogger(ConfigPusher.class);
     private static final int NETCONF_SEND_ATTEMPT_MS_DELAY = 1000;
     private static final int NETCONF_SEND_ATTEMPTS = 20;
 
@@ -134,8 +132,8 @@ public class ConfigPusher {
         final long deadlineNanos = pollingStartNanos + TimeUnit.MILLISECONDS.toNanos(maxWaitForCapabilitiesMillis);
         int attempt = 0;
 
-        String additionalHeader = NetconfMessageAdditionalHeader.toString("unknown", address.getAddress().getHostAddress(),
-                Integer.toString(address.getPort()), "tcp", Optional.of("persister"));
+        NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("unknown", address.getAddress().getHostAddress(),
+                Integer.toString(address.getPort()), "tcp", "persister");
 
         Set<String> latestCapabilities = null;
         while (System.nanoTime() < deadlineNanos) {
index afca333..78586a3 100644 (file)
@@ -10,30 +10,18 @@ package org.opendaylight.controller.netconf.api;
 
 import org.w3c.dom.Document;
 
-import com.google.common.base.Optional;
-
 /**
  * NetconfMessage represents a wrapper around org.w3c.dom.Document. Needed for
  * implementing ProtocolMessage interface.
  */
-public final class NetconfMessage {
-    private final String additionalHeader;
+public class NetconfMessage {
     private final Document doc;
 
     public NetconfMessage(final Document doc) {
-        this(doc, null);
-    }
-
-    public NetconfMessage(Document doc, String additionalHeader) {
         this.doc = doc;
-        this.additionalHeader = additionalHeader;
     }
 
     public Document getDocument() {
         return this.doc;
     }
-
-    public Optional<String> getAdditionalHeader() {
-        return additionalHeader== null ? Optional.<String>absent() : Optional.of(additionalHeader);
-    }
 }
index bff2a54..43664b3 100644 (file)
@@ -18,6 +18,7 @@ import java.io.Closeable;
 import java.net.InetSocketAddress;
 
 import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 import org.opendaylight.protocol.framework.AbstractDispatcher;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
@@ -31,19 +32,23 @@ public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSes
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfClientDispatcher.class);
 
-    private final NetconfClientSessionNegotiatorFactory negotatorFactory;
+    private final NetconfClientSessionNegotiatorFactory negotiatorFactory;
     private final HashedWheelTimer timer;
 
-    public NetconfClientDispatcher(EventLoopGroup bossGroup, EventLoopGroup workerGroup, long clientConnectionTimeoutMillis) {
+    public NetconfClientDispatcher(EventLoopGroup bossGroup, EventLoopGroup workerGroup,
+            long clientConnectionTimeoutMillis) {
         super(bossGroup, workerGroup);
         timer = new HashedWheelTimer();
-        this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.<String>absent(), clientConnectionTimeoutMillis);
+        this.negotiatorFactory = new NetconfClientSessionNegotiatorFactory(timer,
+                Optional.<NetconfHelloMessageAdditionalHeader> absent(), clientConnectionTimeoutMillis);
     }
 
-    public NetconfClientDispatcher(EventLoopGroup bossGroup, EventLoopGroup workerGroup, String additionalHeader, long connectionTimeoutMillis) {
+    public NetconfClientDispatcher(EventLoopGroup bossGroup, EventLoopGroup workerGroup,
+            NetconfHelloMessageAdditionalHeader additionalHeader, long connectionTimeoutMillis) {
         super(bossGroup, workerGroup);
         timer = new HashedWheelTimer();
-        this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.of(additionalHeader), connectionTimeoutMillis);
+        this.negotiatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.of(additionalHeader),
+                connectionTimeoutMillis);
     }
 
     public Future<NetconfClientSession> createClient(InetSocketAddress address,
@@ -57,7 +62,7 @@ public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSes
             }
 
             private void initialize(SocketChannel ch, Promise<NetconfClientSession> promise) {
-                new ClientChannelInitializer( negotatorFactory, sessionListener).initialize(ch, promise);
+                new ClientChannelInitializer(negotiatorFactory, sessionListener).initialize(ch, promise);
             }
         });
     }
@@ -65,7 +70,7 @@ public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSes
     public Future<Void> createReconnectingClient(final InetSocketAddress address,
             final NetconfClientSessionListener listener,
             final ReconnectStrategyFactory connectStrategyFactory, final ReconnectStrategy reestablishStrategy) {
-        final ClientChannelInitializer init = new ClientChannelInitializer(negotatorFactory, listener);
+        final ClientChannelInitializer init = new ClientChannelInitializer(negotiatorFactory, listener);
 
         return super.createReconnectingClient(address, connectStrategyFactory, reestablishStrategy,
                 new PipelineInitializer<NetconfClientSession>() {
@@ -88,14 +93,20 @@ public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSes
         }
 
         @Override
-        protected void initializeAfterDecoder(SocketChannel ch, Promise<NetconfClientSession> promise) {
-            ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(
-                    new SessionListenerFactory<NetconfClientSessionListener>() {
-                        @Override
-                        public NetconfClientSessionListener getSessionListener() {
-                            return sessionListener;
-                        }
-                    }, ch, promise));
+        public void initialize(SocketChannel ch, Promise<NetconfClientSession> promise) {
+                super.initialize(ch,promise);
+        }
+
+        @Override
+        protected void initializeSessionNegotiator(SocketChannel ch, Promise<NetconfClientSession> promise) {
+            ch.pipeline().addAfter(NETCONF_MESSAGE_DECODER,  AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR,
+                    negotiatorFactory.getSessionNegotiator(
+                            new SessionListenerFactory<NetconfClientSessionListener>() {
+                                @Override
+                                public NetconfClientSessionListener getSessionListener() {
+                                    return sessionListener;
+                                }
+                            }, ch, promise));
         }
     }
 
index 3c2e814..c742baf 100644 (file)
@@ -8,10 +8,6 @@
 
 package org.opendaylight.controller.netconf.client;
 
-import io.netty.channel.Channel;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
-
 import java.util.Collection;
 import java.util.List;
 
@@ -19,9 +15,9 @@ import javax.annotation.Nullable;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpression;
 
-import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
 import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
 import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
@@ -32,6 +28,10 @@ import org.w3c.dom.Node;
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+
 public class NetconfClientSessionNegotiator extends
         AbstractNetconfSessionNegotiator<NetconfSessionPreferences, NetconfClientSession, NetconfClientSessionListener> {
 
@@ -71,7 +71,7 @@ public class NetconfClientSessionNegotiator extends
     }
 
     @Override
-    protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel, NetconfMessage message) {
+    protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel, NetconfHelloMessage message) {
         return new NetconfClientSession(sessionListener, channel, extractSessionId(message.getDocument()),
                 getCapabilities(message.getDocument()));
     }
index e678a60..bb372b3 100644 (file)
@@ -17,6 +17,8 @@ import java.io.InputStream;
 
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.protocol.framework.SessionListenerFactory;
 import org.opendaylight.protocol.framework.SessionNegotiator;
@@ -28,11 +30,11 @@ import com.google.common.base.Preconditions;
 
 public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorFactory<NetconfMessage, NetconfClientSession, NetconfClientSessionListener> {
 
-    private final Optional<String> additionalHeader;
+    private final Optional<NetconfHelloMessageAdditionalHeader> additionalHeader;
     private final long connectionTimeoutMillis;
     private final Timer timer;
 
-    public NetconfClientSessionNegotiatorFactory(Timer timer, Optional<String> additionalHeader, long connectionTimeoutMillis) {
+    public NetconfClientSessionNegotiatorFactory(Timer timer, Optional<NetconfHelloMessageAdditionalHeader> additionalHeader, long connectionTimeoutMillis) {
         this.timer = Preconditions.checkNotNull(timer);
         this.additionalHeader = additionalHeader;
         this.connectionTimeoutMillis = connectionTimeoutMillis;
@@ -53,9 +55,12 @@ public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorF
             Promise<NetconfClientSession> promise) {
         // Hello message needs to be recreated every time
         NetconfMessage helloMessage = loadHelloMessageTemplate();
+
         if(this.additionalHeader.isPresent()) {
-            helloMessage = new NetconfMessage(helloMessage.getDocument(), additionalHeader.get());
-        }
+            helloMessage = new NetconfHelloMessage(helloMessage.getDocument(), additionalHeader.get());
+        } else
+            helloMessage = new NetconfHelloMessage(helloMessage.getDocument());
+
         NetconfSessionPreferences proposal = new NetconfSessionPreferences(helloMessage);
         return new NetconfClientSessionNegotiator(proposal, promise, channel, timer,
                 sessionListenerFactory.getSessionListener(), connectionTimeoutMillis);
index 0737279..5b82ff2 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
 import org.opendaylight.controller.netconf.util.handler.ssh.SshHandler;
 import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.controller.netconf.util.handler.ssh.client.Invoker;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
 import org.opendaylight.protocol.framework.SessionListenerFactory;
@@ -31,22 +32,24 @@ public class NetconfSshClientDispatcher extends NetconfClientDispatcher {
 
     private final AuthenticationHandler authHandler;
     private final HashedWheelTimer timer;
-    private final NetconfClientSessionNegotiatorFactory negotatorFactory;
+    private final NetconfClientSessionNegotiatorFactory negotiatorFactory;
 
     public NetconfSshClientDispatcher(AuthenticationHandler authHandler, EventLoopGroup bossGroup,
             EventLoopGroup workerGroup, long connectionTimeoutMillis) {
         super(bossGroup, workerGroup, connectionTimeoutMillis);
         this.authHandler = authHandler;
         this.timer = new HashedWheelTimer();
-        this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.<String>absent(), connectionTimeoutMillis);
+        this.negotiatorFactory = new NetconfClientSessionNegotiatorFactory(timer,
+                Optional.<NetconfHelloMessageAdditionalHeader> absent(), connectionTimeoutMillis);
     }
 
     public NetconfSshClientDispatcher(AuthenticationHandler authHandler, EventLoopGroup bossGroup,
-            EventLoopGroup workerGroup, String additionalHeader, long socketTimeoutMillis) {
+            EventLoopGroup workerGroup, NetconfHelloMessageAdditionalHeader additionalHeader, long socketTimeoutMillis) {
         super(bossGroup, workerGroup, additionalHeader, socketTimeoutMillis);
         this.authHandler = authHandler;
         this.timer = new HashedWheelTimer();
-        this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.of(additionalHeader), socketTimeoutMillis);
+        this.negotiatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.of(additionalHeader),
+                socketTimeoutMillis);
     }
 
     @Override
@@ -56,7 +59,7 @@ public class NetconfSshClientDispatcher extends NetconfClientDispatcher {
 
             @Override
             public void initializeChannel(SocketChannel arg0, Promise<NetconfClientSession> arg1) {
-                new NetconfSshClientInitializer(authHandler, negotatorFactory, sessionListener).initialize(arg0, arg1);
+                new NetconfSshClientInitializer(authHandler, negotiatorFactory, sessionListener).initialize(arg0, arg1);
             }
 
         });
@@ -66,7 +69,7 @@ public class NetconfSshClientDispatcher extends NetconfClientDispatcher {
     public Future<Void> createReconnectingClient(final InetSocketAddress address,
             final NetconfClientSessionListener listener,
             final ReconnectStrategyFactory connectStrategyFactory, final ReconnectStrategy reestablishStrategy) {
-        final NetconfSshClientInitializer init = new NetconfSshClientInitializer(authHandler, negotatorFactory, listener);
+        final NetconfSshClientInitializer init = new NetconfSshClientInitializer(authHandler, negotiatorFactory, listener);
 
         return super.createReconnectingClient(address, connectStrategyFactory, reestablishStrategy,
                 new PipelineInitializer<NetconfClientSession>() {
@@ -103,14 +106,15 @@ public class NetconfSshClientDispatcher extends NetconfClientDispatcher {
         }
 
         @Override
-        protected void initializeAfterDecoder(SocketChannel ch, Promise<NetconfClientSession> promise) {
-            ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(new SessionListenerFactory<NetconfClientSessionListener>() {
+        protected void initializeSessionNegotiator(SocketChannel ch,
+                Promise<NetconfClientSession> promise) {
+            ch.pipeline().addAfter(NETCONF_MESSAGE_DECODER,  AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR,
+                    negotiatorFactory.getSessionNegotiator(new SessionListenerFactory<NetconfClientSessionListener>() {
                 @Override
                 public NetconfClientSessionListener getSessionListener() {
                     return sessionListener;
                 }
             }, ch, promise));
-
         }
     }
 }
index bd39049..ee90097 100644 (file)
@@ -41,6 +41,8 @@ public class NetconfServerDispatcher extends AbstractDispatcher<NetconfServerSes
 
     public static class ServerChannelInitializer extends AbstractChannelInitializer<NetconfServerSession> {
 
+        public static final String DESERIALIZER_EX_HANDLER_KEY = "deserializerExHandler";
+
         private final NetconfServerSessionNegotiatorFactory negotiatorFactory;
         private final NetconfServerSessionListenerFactory listenerFactory;
 
@@ -51,9 +53,14 @@ public class NetconfServerDispatcher extends AbstractDispatcher<NetconfServerSes
         }
 
         @Override
-        protected void initializeAfterDecoder(SocketChannel ch, Promise<NetconfServerSession> promise) {
-            ch.pipeline().addLast("deserializerExHandler", new DeserializerExceptionHandler());
-            ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(listenerFactory, ch, promise));
+        protected void initializeMessageDecoder(SocketChannel ch) {
+            super.initializeMessageDecoder(ch);
+            ch.pipeline().addLast(DESERIALIZER_EX_HANDLER_KEY, new DeserializerExceptionHandler());
+        }
+
+        @Override
+        protected void initializeSessionNegotiator(SocketChannel ch, Promise<NetconfServerSession> promise) {
+            ch.pipeline().addAfter(DESERIALIZER_EX_HANDLER_KEY, AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR, negotiatorFactory.getSessionNegotiator(listenerFactory, ch, promise));
         }
     }
 
index 93d4e55..9ddc64f 100644 (file)
@@ -17,6 +17,7 @@ import java.util.regex.Pattern;
 
 import org.opendaylight.controller.netconf.api.AbstractNetconfSession;
 import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.NetconfTcp;
@@ -38,13 +39,13 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfServerSession.class);
 
-    private final NetconfServerSessionNegotiator.AdditionalHeader header;
+    private final NetconfHelloMessageAdditionalHeader header;
 
     private Date loginTime;
     private long inRpcSuccess, inRpcFail, outRpcError;
 
     public NetconfServerSession(NetconfServerSessionListener sessionListener, Channel channel, long sessionId,
-            NetconfServerSessionNegotiator.AdditionalHeader header) {
+            NetconfHelloMessageAdditionalHeader header) {
         super(sessionListener, channel, sessionId);
         this.header = header;
         logger.debug("Session {} created", toString());
@@ -71,6 +72,9 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
 
     public static final String ISO_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
 
+    private static final String dateTimePatternString = DateAndTime.PATTERN_CONSTANTS.get(0);
+    private static final Pattern dateTimePattern = Pattern.compile(dateTimePatternString);
+
     @Override
     public Session toManagementSession() {
         SessionBuilder builder = new SessionBuilder();
@@ -80,16 +84,16 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
 
         Preconditions.checkState(DateAndTime.PATTERN_CONSTANTS.size() == 1);
         String formattedDateTime = formatDateTime(loginTime);
-        String pattern = DateAndTime.PATTERN_CONSTANTS.get(0);
-        Matcher matcher = Pattern.compile(pattern).matcher(formattedDateTime);
-        Preconditions.checkState(matcher.matches(), "Formatted datetime %s does not match pattern %s", formattedDateTime, pattern);
+
+        Matcher matcher = dateTimePattern.matcher(formattedDateTime);
+        Preconditions.checkState(matcher.matches(), "Formatted datetime %s does not match pattern %s", formattedDateTime, dateTimePattern);
         builder.setLoginTime(new DateAndTime(formattedDateTime));
 
         builder.setInBadRpcs(new ZeroBasedCounter32(inRpcFail));
         builder.setInRpcs(new ZeroBasedCounter32(inRpcSuccess));
         builder.setOutRpcErrors(new ZeroBasedCounter32(outRpcError));
 
-        builder.setUsername(header.getUsername());
+        builder.setUsername(header.getUserName());
         builder.setTransport(getTransportForString(header.getTransport()));
 
         builder.setOutNotifications(new ZeroBasedCounter32(0L));
@@ -97,7 +101,7 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
         builder.setKey(new SessionKey(getSessionId()));
 
         Session1Builder builder1 = new Session1Builder();
-        builder1.setSessionIdentifier(header.getSessionType());
+        builder1.setSessionIdentifier(header.getSessionIdentifier());
         builder.addAugmentation(Session1.class, builder1.build());
 
         return builder.build();
index 1303d11..1b4dfff 100644 (file)
@@ -8,21 +8,21 @@
 
 package org.opendaylight.controller.netconf.impl;
 
-import io.netty.channel.Channel;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
-
 import java.net.InetSocketAddress;
 
-import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
-import org.opendaylight.controller.netconf.impl.util.AdditionalHeaderUtil;
 import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Optional;
 
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+
 public class NetconfServerSessionNegotiator extends
         AbstractNetconfSessionNegotiator<NetconfServerSessionPreferences, NetconfServerSession, NetconfServerSessionListener> {
 
@@ -35,60 +35,21 @@ public class NetconfServerSessionNegotiator extends
     }
 
     @Override
-    protected NetconfServerSession getSession(NetconfServerSessionListener sessionListener, Channel channel, NetconfMessage message) {
-        Optional<String> additionalHeader = message.getAdditionalHeader();
+    protected NetconfServerSession getSession(NetconfServerSessionListener sessionListener, Channel channel, NetconfHelloMessage message) {
+        Optional<NetconfHelloMessageAdditionalHeader> additionalHeader = message.getAdditionalHeader();
 
-        AdditionalHeader parsedHeader;
+        NetconfHelloMessageAdditionalHeader parsedHeader;
         if (additionalHeader.isPresent()) {
-            parsedHeader = AdditionalHeaderUtil.fromString(additionalHeader.get());
+            parsedHeader = additionalHeader.get();
         } else {
-            parsedHeader = new AdditionalHeader("unknown", ((InetSocketAddress)channel.localAddress()).getHostString(),
+            InetSocketAddress inetSocketAddress = (InetSocketAddress) channel.localAddress();
+            parsedHeader = new NetconfHelloMessageAdditionalHeader("unknown", inetSocketAddress.getHostString(), Integer.toString(inetSocketAddress.getPort()),
                     "tcp", "client");
         }
+
         logger.debug("Additional header from hello parsed as {} from {}", parsedHeader, additionalHeader);
 
         return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId(), parsedHeader);
     }
 
-    public static class AdditionalHeader {
-
-        private final String username;
-        private final String address;
-        private final String transport;
-        private final String sessionIdentifier;
-
-        public AdditionalHeader(String userName, String hostAddress, String transport, String sessionIdentifier) {
-            this.address = hostAddress;
-            this.username = userName;
-            this.transport = transport;
-            this.sessionIdentifier = sessionIdentifier;
-        }
-
-        String getUsername() {
-            return username;
-        }
-
-        String getAddress() {
-            return address;
-        }
-
-        String getTransport() {
-            return transport;
-        }
-
-        String getSessionType() {
-            return sessionIdentifier;
-        }
-
-        @Override
-        public String toString() {
-            final StringBuffer sb = new StringBuffer("AdditionalHeader{");
-            sb.append("username='").append(username).append('\'');
-            sb.append(", address='").append(address).append('\'');
-            sb.append(", transport='").append(transport).append('\'');
-            sb.append('}');
-            return sb.toString();
-        }
-    }
-
-}
+   }
index 8086b74..e052f61 100644 (file)
@@ -8,20 +8,15 @@
 
 package org.opendaylight.controller.netconf.impl;
 
+import com.google.common.base.Preconditions;
 import io.netty.channel.Channel;
 import io.netty.util.Timer;
 import io.netty.util.concurrent.Promise;
-
-import java.io.InputStream;
-
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
-
-import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
 import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
 import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
 import org.opendaylight.controller.netconf.util.NetconfUtil;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
 import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -32,9 +27,11 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
-import com.google.common.base.Preconditions;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import java.io.InputStream;
 
-public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorFactory<NetconfMessage, NetconfServerSession, NetconfServerSessionListener> {
+public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorFactory<NetconfHelloMessage, NetconfServerSession, NetconfServerSessionListener> {
 
     public static final String SERVER_HELLO_XML_LOCATION = "/server_hello.xml";
 
@@ -77,7 +74,7 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF
     private static final XPathExpression capabilitiesXPath = XMLNetconfUtil
             .compileXPath("/netconf:hello/netconf:capabilities");
 
-    private NetconfMessage createHelloMessage(long sessionId) {
+    private NetconfHelloMessage createHelloMessage(long sessionId) {
         Document helloMessageTemplate = getHelloTemplateClone();
 
         // change session ID
@@ -96,7 +93,7 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF
             capabilityElement.setTextContent(capability);
             capabilitiesElement.appendChild(capabilityElement);
         }
-        return new NetconfMessage(helloMessageTemplate);
+        return new NetconfHelloMessage(helloMessageTemplate);
     }
 
     private synchronized Document getHelloTemplateClone() {
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/AdditionalHeaderUtil.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/AdditionalHeaderUtil.java
deleted file mode 100644 (file)
index 5c630dd..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.netconf.impl.util;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiator.AdditionalHeader;
-
-import com.google.common.base.Preconditions;
-
-public class AdditionalHeaderUtil {
-
-    private static final Pattern pattern = Pattern
-            .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+)[^\\]]+\\]");
-    private static final Pattern customHeaderPattern = Pattern
-            .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+);(?<sessionIdentifier>[a-z]+)[^\\]]+\\]");
-
-    public static AdditionalHeader fromString(String additionalHeader) {
-        additionalHeader = additionalHeader.trim();
-        Matcher matcher = pattern.matcher(additionalHeader);
-        Matcher matcher2 = customHeaderPattern.matcher(additionalHeader);
-        Preconditions.checkArgument(matcher.matches(), "Additional header in wrong format %s, expected %s",
-                additionalHeader, pattern);
-        String username = matcher.group("username");
-        String address = matcher.group("address");
-        String transport = matcher.group("transport");
-        String sessionIdentifier = "client";
-        if (matcher2.matches()) {
-            sessionIdentifier = matcher2.group("sessionIdentifier");
-        }
-        return new AdditionalHeader(username, address, transport, sessionIdentifier);
-    }
-
-}
index 97d9a98..c2dcd67 100644 (file)
@@ -10,15 +10,15 @@ package org.opendaylight.controller.netconf.impl;
 import junit.framework.Assert;
 
 import org.junit.Test;
-import org.opendaylight.controller.netconf.impl.util.AdditionalHeaderUtil;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 
 public class AdditionalHeaderParserTest {
 
     @Test
     public void testParsing() throws Exception {
         String s = "[netconf;10.12.0.102:48528;ssh;;;;;;]";
-        NetconfServerSessionNegotiator.AdditionalHeader header = AdditionalHeaderUtil.fromString(s);
-        Assert.assertEquals("netconf", header.getUsername());
+        NetconfHelloMessageAdditionalHeader header = NetconfHelloMessageAdditionalHeader.fromString(s);
+        Assert.assertEquals("netconf", header.getUserName());
         Assert.assertEquals("10.12.0.102", header.getAddress());
         Assert.assertEquals("ssh", header.getTransport());
     }
@@ -26,8 +26,8 @@ public class AdditionalHeaderParserTest {
     @Test
     public void testParsing2() throws Exception {
         String s = "[tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]";
-        NetconfServerSessionNegotiator.AdditionalHeader header = AdditionalHeaderUtil.fromString(s);
-        Assert.assertEquals("tomas", header.getUsername());
+        NetconfHelloMessageAdditionalHeader header = NetconfHelloMessageAdditionalHeader.fromString(s);
+        Assert.assertEquals("tomas", header.getUserName());
         Assert.assertEquals("10.0.0.0", header.getAddress());
         Assert.assertEquals("tcp", header.getTransport());
     }
@@ -35,6 +35,6 @@ public class AdditionalHeaderParserTest {
     @Test(expected = IllegalArgumentException.class)
     public void testParsingNoUsername() throws Exception {
         String s = "[10.12.0.102:48528;ssh;;;;;;]";
-        AdditionalHeaderUtil.fromString(s);
+        NetconfHelloMessageAdditionalHeader.fromString(s);
     }
 }
index 0ef2c28..c0d52ad 100644 (file)
@@ -37,6 +37,7 @@ import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.slf4j.Logger;
@@ -100,7 +101,8 @@ public class ConcurrentClientsTest {
         }
 
         nettyGroup = new NioEventLoopGroup();
-        netconfClientDispatcher = new NetconfClientDispatcher( nettyGroup, nettyGroup, 5000);
+        NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("uname", "10.10.10.1", "830", "tcp", "client");
+        netconfClientDispatcher = new NetconfClientDispatcher( nettyGroup, nettyGroup, additionalHeader, 5000);
 
         NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
         factoriesListener.onAddNetconfOperationServiceFactory(mockOpF());
index 7068de8..0910d94 100644 (file)
@@ -13,21 +13,44 @@ import io.netty.util.concurrent.Promise;
 
 import org.opendaylight.controller.netconf.api.NetconfSession;
 import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory;
+import org.opendaylight.controller.netconf.util.handler.NetconfHelloMessageToXMLEncoder;
 import org.opendaylight.controller.netconf.util.handler.NetconfMessageAggregator;
-import org.opendaylight.controller.netconf.util.handler.NetconfMessageToXMLEncoder;
-import org.opendaylight.controller.netconf.util.handler.NetconfXMLToMessageDecoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfXMLToHelloMessageDecoder;
 import org.opendaylight.controller.netconf.util.messages.FramingMechanism;
 
 public abstract class AbstractChannelInitializer<S extends NetconfSession> {
 
-    public void initialize(SocketChannel ch, Promise<S> promise){
-        ch.pipeline().addLast("aggregator", new NetconfMessageAggregator(FramingMechanism.EOM));
-        ch.pipeline().addLast(new NetconfXMLToMessageDecoder());
-        initializeAfterDecoder(ch, promise);
-        ch.pipeline().addLast("frameEncoder", FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM));
-        ch.pipeline().addLast(new NetconfMessageToXMLEncoder());
+    public static final String NETCONF_MESSAGE_DECODER = "netconfMessageDecoder";
+    public static final String NETCONF_MESSAGE_AGGREGATOR = "aggregator";
+    public static final String NETCONF_MESSAGE_ENCODER = "netconfMessageEncoder";
+    public static final String NETCONF_MESSAGE_FRAME_ENCODER = "frameEncoder";
+    public static final String NETCONF_SESSION_NEGOTIATOR = "negotiator";
+
+    public void initialize(SocketChannel ch, Promise<S> promise) {
+        ch.pipeline().addLast(NETCONF_MESSAGE_AGGREGATOR, new NetconfMessageAggregator(FramingMechanism.EOM));
+        initializeMessageDecoder(ch);
+        ch.pipeline().addLast(NETCONF_MESSAGE_FRAME_ENCODER, FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM));
+        initializeMessageEncoder(ch);
+
+        initializeSessionNegotiator(ch, promise);
+    }
+
+    protected void initializeMessageEncoder(SocketChannel ch) {
+        // Special encoding handler for hello message to include additional header if available,
+        // it is thrown away after successful negotiation
+        ch.pipeline().addLast(NETCONF_MESSAGE_ENCODER, new NetconfHelloMessageToXMLEncoder());
+    }
+
+    protected void initializeMessageDecoder(SocketChannel ch) {
+        // Special decoding handler for hello message to parse additional header if available,
+        // it is thrown away after successful negotiation
+        ch.pipeline().addLast(NETCONF_MESSAGE_DECODER, new NetconfXMLToHelloMessageDecoder());
     }
 
-    protected abstract void initializeAfterDecoder(SocketChannel ch, Promise<S> promise);
+    /**
+     * Insert session negotiator into the pipeline. It must be inserted after message decoder
+     * identified by {@link AbstractChannelInitializer#NETCONF_MESSAGE_DECODER}, (or any other custom decoder processor)
+     */
+    protected abstract void initializeSessionNegotiator(SocketChannel ch, Promise<S> promise);
 
 }
index 9c35c72..9986b82 100644 (file)
@@ -8,17 +8,6 @@
 
 package org.opendaylight.controller.netconf.util;
 
-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 io.netty.util.TimerTask;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.GenericFutureListener;
-import io.netty.util.concurrent.Promise;
-
 import java.util.concurrent.TimeUnit;
 
 import org.opendaylight.controller.netconf.api.AbstractNetconfSession;
@@ -26,11 +15,12 @@ import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.NetconfSessionListener;
 import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
 import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
 import org.opendaylight.controller.netconf.util.handler.NetconfMessageAggregator;
 import org.opendaylight.controller.netconf.util.handler.NetconfMessageChunkDecoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageToXMLEncoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfXMLToMessageDecoder;
 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.opendaylight.protocol.framework.AbstractSessionNegotiator;
 import org.slf4j.Logger;
@@ -41,11 +31,23 @@ import org.w3c.dom.NodeList;
 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 io.netty.util.TimerTask;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import io.netty.util.concurrent.Promise;
+
 public abstract class AbstractNetconfSessionNegotiator<P extends NetconfSessionPreferences, S extends AbstractNetconfSession<S, L>, L extends NetconfSessionListener<S>>
-extends AbstractSessionNegotiator<NetconfMessage, S> {
+extends AbstractSessionNegotiator<NetconfHelloMessage, S> {
 
     private static final Logger logger = LoggerFactory.getLogger(AbstractNetconfSessionNegotiator.class);
     public static final String NAME_OF_EXCEPTION_HANDLER = "lastExceptionHandler";
+    public static final String CHUNK_DECODER_CHANNEL_HANDLER_KEY = "chunkDecoder";
 
     protected final P sessionPreferences;
 
@@ -147,20 +149,21 @@ extends AbstractSessionNegotiator<NetconfMessage, S> {
     }
 
     @Override
-    protected void handleMessage(NetconfMessage netconfMessage) {
+    protected void handleMessage(NetconfHelloMessage netconfMessage) {
         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());
+        // Only Hello message should arrive during negotiation
+        if (netconfMessage instanceof NetconfHelloMessage) {
+
+            replaceHelloMessageHandlers();
+
+            if (shouldUseChunkFraming(doc)) {
+                insertChunkFramingToPipeline();
             }
+
             changeState(State.ESTABLISHED);
-            S session = getSession(sessionListener, channel, netconfMessage);
+            S session = getSession(sessionListener, channel, (NetconfHelloMessage)netconfMessage);
+
             negotiationSuccessful(session);
         } else {
             final IllegalStateException cause = new IllegalStateException(
@@ -170,19 +173,37 @@ extends AbstractSessionNegotiator<NetconfMessage, S> {
         }
     }
 
-    protected abstract S getSession(L sessionListener, Channel channel, NetconfMessage message);
+    /**
+     * Insert chunk framing handlers into the pipeline
+     */
+    private void insertChunkFramingToPipeline() {
+        replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_FRAME_ENCODER,
+                FramingMechanismHandlerFactory.createHandler(FramingMechanism.CHUNK));
+        replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_AGGREGATOR,
+                new NetconfMessageAggregator(FramingMechanism.CHUNK));
+        channel.pipeline().addAfter(AbstractChannelInitializer.NETCONF_MESSAGE_AGGREGATOR,
+                CHUNK_DECODER_CHANNEL_HANDLER_KEY, new NetconfMessageChunkDecoder());
+    }
+
+    private boolean shouldUseChunkFraming(Document doc) {
+        return containsBase11Capability(doc)
+                && containsBase11Capability(sessionPreferences.getHelloMessage().getDocument());
+    }
 
-    private boolean isHelloMessage(Document doc) {
-        try {
-            XmlElement.fromDomElementWithExpected(doc.getDocumentElement(), "hello",
-                    XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+    /**
+     * Remove special handlers for hello message. Insert regular netconf xml message (en|de)coders.
+     */
+    private void replaceHelloMessageHandlers() {
+        replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, new NetconfXMLToMessageDecoder());
+        replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_ENCODER, new NetconfMessageToXMLEncoder());
+    }
 
-        } catch (IllegalArgumentException | IllegalStateException e) {
-            return false;
-        }
-        return true;
+    private static ChannelHandler replaceChannelHandler(Channel channel, String handlerKey, ChannelHandler decoder) {
+        return channel.pipeline().replace(handlerKey, handlerKey, decoder);
     }
 
+    protected abstract S getSession(L sessionListener, Channel channel, NetconfHelloMessage message);
+
     private synchronized void changeState(final State newState) {
         logger.debug("Changing state from : {} to : {}", state, newState);
         Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s", state,
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfHelloMessageToXMLEncoder.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfHelloMessageToXMLEncoder.java
new file mode 100644 (file)
index 0000000..a87d175
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.util.handler;
+
+import java.nio.ByteBuffer;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+/**
+ * Customized NetconfMessageToXMLEncoder that serializes additional header with
+ * session metadata along with
+ * {@link org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage}
+ * . Used by netconf clients to send information about the user, ip address,
+ * protocol etc.
+ * <p/>
+ * Hello message with header example:
+ * <p/>
+ *
+ * <pre>
+ * {@code
+ * [tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]
+ * <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ * <capabilities>
+ * <capability>urn:ietf:params:netconf:base:1.0</capability>
+ * </capabilities>
+ * </hello>
+ * }
+ * </pre>
+ */
+public final class NetconfHelloMessageToXMLEncoder extends NetconfMessageToXMLEncoder {
+
+    @Override
+    protected ByteBuffer encodeMessage(NetconfMessage msg) {
+        Preconditions.checkState(msg instanceof NetconfHelloMessage, "Netconf message of type %s expected, was %s",
+                NetconfHelloMessage.class, msg.getClass());
+        Optional<NetconfHelloMessageAdditionalHeader> headerOptional = ((NetconfHelloMessage) msg)
+                .getAdditionalHeader();
+
+        // If additional header present, serialize it along with netconf hello
+        // message
+        if (headerOptional.isPresent()) {
+            byte[] bytesFromHeader = headerOptional.get().toFormattedString().getBytes(Charsets.UTF_8);
+            byte[] bytesFromMessage = xmlToString(msg.getDocument()).getBytes(Charsets.UTF_8);
+
+            ByteBuffer byteBuffer = ByteBuffer.allocate(bytesFromHeader.length + bytesFromMessage.length)
+                    .put(bytesFromHeader).put(bytesFromMessage);
+            byteBuffer.flip();
+            return byteBuffer;
+        }
+
+        return super.encodeMessage(msg);
+    }
+}
index 31a4225..df0f7ef 100644 (file)
@@ -24,7 +24,7 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Charsets;
 import com.google.common.base.Optional;
 
-public final class NetconfMessageToXMLEncoder extends MessageToByteEncoder<NetconfMessage> {
+public class NetconfMessageToXMLEncoder extends MessageToByteEncoder<NetconfMessage> {
     private static final Logger LOG = LoggerFactory.getLogger(NetconfMessageToXMLEncoder.class);
 
     private final Optional<String> clientId;
@@ -47,21 +47,17 @@ public final class NetconfMessageToXMLEncoder extends MessageToByteEncoder<Netco
             msg.getDocument().appendChild(comment);
         }
 
-        final ByteBuffer msgBytes;
-        if(msg.getAdditionalHeader().isPresent()) {
-            final String header = msg.getAdditionalHeader().get();
-            LOG.trace("Header of netconf message parsed \n{}", header);
-            // FIXME: this can be written in pieces
-            msgBytes = Charsets.UTF_8.encode(header + xmlToString(msg.getDocument()));
-        } else {
-            msgBytes = Charsets.UTF_8.encode(xmlToString(msg.getDocument()));
-        }
+        final ByteBuffer msgBytes = encodeMessage(msg);
 
         LOG.trace("Putting message \n{}", xmlToString(msg.getDocument()));
         out.writeBytes(msgBytes);
     }
 
-    private String xmlToString(Document doc) {
+    protected ByteBuffer encodeMessage(NetconfMessage msg) {
+        return Charsets.UTF_8.encode(xmlToString(msg.getDocument()));
+    }
+
+    protected String xmlToString(Document doc) {
         return XmlUtil.toString(doc, false);
     }
 }
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfXMLToHelloMessageDecoder.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfXMLToHelloMessageDecoder.java
new file mode 100644 (file)
index 0000000..42586a5
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.util.handler;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
+import org.w3c.dom.Document;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Customized NetconfXMLToMessageDecoder that reads additional header with
+ * session metadata from
+ * {@link org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage}
+ * . Used by netconf server to retrieve information about session metadata.
+ */
+public class NetconfXMLToHelloMessageDecoder extends NetconfXMLToMessageDecoder {
+
+    private static final List<byte[]> POSSIBLE_ENDS = ImmutableList.of(
+            new byte[] { ']', '\n' },
+            new byte[] { ']', '\r', '\n' });
+    private static final List<byte[]> POSSIBLE_STARTS = ImmutableList.of(
+            new byte[] { '[' },
+            new byte[] { '\r', '\n', '[' },
+            new byte[] { '\n', '[' });
+
+    private String additionalHeaderCache;
+
+    @Override
+    protected byte[] preprocessMessageBytes(byte[] bytes) {
+        // Extract bytes containing header with additional metadata
+
+        if (startsWithAdditionalHeader(bytes)) {
+            // Auth information containing username, ip address... extracted for monitoring
+            int endOfAuthHeader = getAdditionalHeaderEndIndex(bytes);
+            if (endOfAuthHeader > -1) {
+                byte[] additionalHeaderBytes = Arrays.copyOfRange(bytes, 0, endOfAuthHeader + 2);
+                additionalHeaderCache = additionalHeaderToString(additionalHeaderBytes);
+                bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
+            }
+        }
+
+        return bytes;
+    }
+
+    @Override
+    protected void cleanUpAfterDecode() {
+        additionalHeaderCache = null;
+    }
+
+    @Override
+    protected NetconfMessage buildNetconfMessage(Document doc) {
+        return new NetconfHelloMessage(doc, additionalHeaderCache == null ? null
+                : NetconfHelloMessageAdditionalHeader.fromString(additionalHeaderCache));
+    }
+
+    private int getAdditionalHeaderEndIndex(byte[] bytes) {
+        for (byte[] possibleEnd : POSSIBLE_ENDS) {
+            int idx = findByteSequence(bytes, possibleEnd);
+
+            if (idx != -1) {
+                return idx;
+            }
+        }
+
+        return -1;
+    }
+
+    private static int findByteSequence(final byte[] bytes, final byte[] sequence) {
+        if (bytes.length < sequence.length) {
+            throw new IllegalArgumentException("Sequence to be found is longer than the given byte array.");
+        }
+        if (bytes.length == sequence.length) {
+            if (Arrays.equals(bytes, sequence)) {
+                return 0;
+            } else {
+                return -1;
+            }
+        }
+        int j = 0;
+        for (int i = 0; i < bytes.length; i++) {
+            if (bytes[i] == sequence[j]) {
+                j++;
+                if (j == sequence.length) {
+                    return i - j + 1;
+                }
+            } else {
+                j = 0;
+            }
+        }
+        return -1;
+    }
+
+    private boolean startsWithAdditionalHeader(byte[] bytes) {
+        for (byte[] possibleStart : POSSIBLE_STARTS) {
+            int i = 0;
+            for (byte b : possibleStart) {
+                if(bytes[i++] != b)
+                    break;
+
+                if(i == possibleStart.length)
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    private String additionalHeaderToString(byte[] bytes) {
+        return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+    }
+
+}
index 2eefb91..b697edf 100644 (file)
@@ -7,15 +7,8 @@
  */
 package org.opendaylight.controller.netconf.util.handler;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageDecoder;
-
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.util.Arrays;
 import java.util.List;
 
 import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
@@ -24,22 +17,17 @@ import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableList;
 
-public final class NetconfXMLToMessageDecoder extends ByteToMessageDecoder {
-    private static final Logger LOG = LoggerFactory.getLogger(NetconfXMLToMessageDecoder.class);
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
 
-    private static final List<byte[]> POSSIBLE_ENDS = ImmutableList.of(
-            new byte[] { ']', '\n' },
-            new byte[] { ']', '\r', '\n' });
-    private static final List<byte[]> POSSIBLE_STARTS = ImmutableList.of(
-            new byte[] { '[' },
-            new byte[] { '\r', '\n', '[' },
-            new byte[] { '\n', '[' });
+public class NetconfXMLToMessageDecoder extends ByteToMessageDecoder {
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfXMLToMessageDecoder.class);
 
     @Override
     @VisibleForTesting
@@ -57,93 +45,35 @@ public final class NetconfXMLToMessageDecoder extends ByteToMessageDecoder {
 
             logMessage(bytes);
 
-            String additionalHeader = null;
-
-            // FIXME: this has to be moved into the negotiator and explained as to what the heck
-            // is going on. This is definitely not specified in NETCONF and has no place here. It
-            // requires reading all data and incurs inefficiency by being unable to pipe the ByteBuf
-            // directly into the XML decoder.
-            if (startsWithAdditionalHeader(bytes)) {
-                // Auth information containing username, ip address... extracted for monitoring
-                int endOfAuthHeader = getAdditionalHeaderEndIndex(bytes);
-                if (endOfAuthHeader > -1) {
-                    byte[] additionalHeaderBytes = Arrays.copyOfRange(bytes, 0, endOfAuthHeader + 2);
-                    additionalHeader = additionalHeaderToString(additionalHeaderBytes);
-                    bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
-                }
-            }
+            bytes = preprocessMessageBytes(bytes);
             NetconfMessage message;
             try {
                 Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes));
-                message = new NetconfMessage(doc, additionalHeader);
-            } catch (final SAXException | IOException | IllegalStateException e) {
+                message = buildNetconfMessage(doc);
+            } catch (Exception e) {
                 throw new NetconfDeserializerException("Could not parse message from " + new String(bytes), e);
             }
 
             out.add(message);
         } finally {
             in.discardReadBytes();
+            cleanUpAfterDecode();
         }
     }
 
-    private int getAdditionalHeaderEndIndex(byte[] bytes) {
-        for (byte[] possibleEnd : POSSIBLE_ENDS) {
-            int idx = findByteSequence(bytes, possibleEnd);
-
-            if (idx != -1) {
-                return idx;
-            }
-        }
+    protected void cleanUpAfterDecode() {}
 
-        return -1;
+    protected NetconfMessage buildNetconfMessage(Document doc) {
+        return new NetconfMessage(doc);
     }
 
-    private static int findByteSequence(final byte[] bytes, final byte[] sequence) {
-        if (bytes.length < sequence.length) {
-            throw new IllegalArgumentException("Sequence to be found is longer than the given byte array.");
-        }
-        if (bytes.length == sequence.length) {
-            if (Arrays.equals(bytes, sequence)) {
-                return 0;
-            } else {
-                return -1;
-            }
-        }
-        int j = 0;
-        for (int i = 0; i < bytes.length; i++) {
-            if (bytes[i] == sequence[j]) {
-                j++;
-                if (j == sequence.length) {
-                    return i - j + 1;
-                }
-            } else {
-                j = 0;
-            }
-        }
-        return -1;
+    protected byte[] preprocessMessageBytes(byte[] bytes) {
+        return bytes;
     }
 
-    private boolean startsWithAdditionalHeader(byte[] bytes) {
-        for (byte[] possibleStart : POSSIBLE_STARTS) {
-            int i = 0;
-            for (byte b : possibleStart) {
-                if(bytes[i] != b)
-                    break;
-
-                return true;
-            }
-        }
-
-        return false;
-    };
-
     private void logMessage(byte[] bytes) {
         String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
         LOG.debug("Parsing message \n{}", s);
     }
 
-    private String additionalHeaderToString(byte[] bytes) {
-        return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
-    }
-
 }
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java
new file mode 100644 (file)
index 0000000..249f894
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.util.messages;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+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.w3c.dom.Document;
+
+import com.google.common.base.Optional;
+
+/**
+ * NetconfMessage that can carry additional header with session metadata. See {@link org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader}
+ */
+public final class NetconfHelloMessage extends NetconfMessage {
+
+    public static final String HELLO_TAG = "hello";
+
+    private final NetconfHelloMessageAdditionalHeader additionalHeader;
+
+    public NetconfHelloMessage(Document doc, NetconfHelloMessageAdditionalHeader additionalHeader) {
+        super(doc);
+        checkHelloMessage(doc);
+        this.additionalHeader = additionalHeader;
+    }
+
+    public NetconfHelloMessage(Document doc) {
+        this(doc, null);
+    }
+
+    public Optional<NetconfHelloMessageAdditionalHeader> getAdditionalHeader() {
+        return additionalHeader== null ? Optional.<NetconfHelloMessageAdditionalHeader>absent() : Optional.of(additionalHeader);
+    }
+
+    private static void checkHelloMessage(Document doc) {
+        try {
+            XmlElement.fromDomElementWithExpected(doc.getDocumentElement(), HELLO_TAG,
+                    XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw new IllegalArgumentException(String.format(
+                    "Hello message invalid format, should contain %s tag from namespace %s, but is: %s", HELLO_TAG,
+                    XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, XmlUtil.toString(doc)), e);
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessageAdditionalHeader.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessageAdditionalHeader.java
new file mode 100644 (file)
index 0000000..f3ca30d
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.util.messages;
+
+import com.google.common.base.Preconditions;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Additional header can be used with hello message to carry information about
+ * session's connection. Provided information can be reported via netconf
+ * monitoring.
+ * <pre>
+ * It has pattern "[username; host-address:port; transport; session-identifier;]"
+ * username - name of account on a remote
+ * host-address - client's IP address
+ * port - port number
+ * transport - tcp, ssh
+ * session-identifier - persister, client
+ * Session-identifier is optional, others mandatory.
+ * </pre>
+ * This header is inserted in front of a netconf hello message followed by a newline.
+ */
+public class NetconfHelloMessageAdditionalHeader {
+
+    private static final String SC = ";";
+
+    private final String userName;
+    private final String hostAddress;
+    private final String port;
+    private final String transport;
+    private final String sessionIdentifier;
+
+    public NetconfHelloMessageAdditionalHeader(String userName, String hostAddress, String port, String transport, String sessionIdentifier) {
+        this.userName = userName;
+        this.hostAddress = hostAddress;
+        this.port = port;
+        this.transport = transport;
+        this.sessionIdentifier = sessionIdentifier;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public String getAddress() {
+        return hostAddress;
+    }
+
+    public String getPort() {
+        return port;
+    }
+
+    public String getTransport() {
+        return transport;
+    }
+
+    public String getSessionIdentifier() {
+        return sessionIdentifier;
+    }
+
+    /**
+     * Format additional header into a string suitable as a prefix for netconf hello message
+     */
+    public String toFormattedString() {
+        Preconditions.checkNotNull(userName);
+        Preconditions.checkNotNull(hostAddress);
+        Preconditions.checkNotNull(port);
+        Preconditions.checkNotNull(transport);
+        Preconditions.checkNotNull(sessionIdentifier);
+        return "[" + userName + SC + hostAddress + ":" + port + SC + transport + SC + sessionIdentifier + SC + "]"
+                + System.lineSeparator();
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer("NetconfHelloMessageAdditionalHeader{");
+        sb.append("userName='").append(userName).append('\'');
+        sb.append(", hostAddress='").append(hostAddress).append('\'');
+        sb.append(", port='").append(port).append('\'');
+        sb.append(", transport='").append(transport).append('\'');
+        sb.append(", sessionIdentifier='").append(sessionIdentifier).append('\'');
+        sb.append('}');
+        return sb.toString();
+    }
+
+    // TODO IPv6
+    private static final Pattern pattern = Pattern
+            .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+)[^\\]]+\\]");
+    private static final Pattern customHeaderPattern = Pattern
+            .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+);(?<sessionIdentifier>[a-z]+)[^\\]]+\\]");
+
+    /**
+     * Parse additional header from a formatted string
+     */
+    public static NetconfHelloMessageAdditionalHeader fromString(String additionalHeader) {
+        additionalHeader = additionalHeader.trim();
+        Matcher matcher = pattern.matcher(additionalHeader);
+        Matcher matcher2 = customHeaderPattern.matcher(additionalHeader);
+        Preconditions.checkArgument(matcher.matches(), "Additional header in wrong format %s, expected %s",
+                additionalHeader, pattern);
+
+        String username = matcher.group("username");
+        String address = matcher.group("address");
+        String port = matcher.group("port");
+        String transport = matcher.group("transport");
+        String sessionIdentifier = "client";
+        if (matcher2.matches()) {
+            sessionIdentifier = matcher2.group("sessionIdentifier");
+        }
+        return new NetconfHelloMessageAdditionalHeader(username, address, port, transport, sessionIdentifier);
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageAdditionalHeader.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageAdditionalHeader.java
deleted file mode 100644 (file)
index 457e226..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.netconf.util.messages;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
-/**
- * Additional header can be used with hello message to carry information about
- * session's connection. Provided information can be reported via netconf
- * monitoring.
- * <pre>
- * It has pattern "[username; host-address:port; transport; session-identifier;]"
- * username - name of account on a remote
- * host-address - client's IP address
- * port - port number
- * transport - tcp, ssh
- * session-identifier - persister, client
- * Session-identifier is optional, others mandatory.
- * </pre>
- */
-public class NetconfMessageAdditionalHeader {
-
-    private static final String SC = ";";
-
-    public static String toString(String userName, String hostAddress, String port, String transport,
-            Optional<String> sessionIdentifier) {
-        Preconditions.checkNotNull(userName);
-        Preconditions.checkNotNull(hostAddress);
-        Preconditions.checkNotNull(port);
-        Preconditions.checkNotNull(transport);
-        String identifier = sessionIdentifier.isPresent() ? sessionIdentifier.get() : "";
-        return "[" + userName + SC + hostAddress + ":" + port + SC + transport + SC + identifier + SC + "]"
-                + System.lineSeparator();
-    }
-}
index c405d9b..6b7bffc 100644 (file)
@@ -15,6 +15,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.junit.Test;
+import org.opendaylight.controller.netconf.util.handler.NetconfXMLToHelloMessageDecoder;
 import org.opendaylight.controller.netconf.util.handler.NetconfXMLToMessageDecoder;
 
 import com.google.common.io.Files;
@@ -22,7 +23,7 @@ import com.google.common.io.Files;
 public class NetconfMessageFactoryTest {
     @Test
     public void testAuth() throws Exception {
-        NetconfXMLToMessageDecoder parser = new NetconfXMLToMessageDecoder();
+        NetconfXMLToMessageDecoder parser = new NetconfXMLToHelloMessageDecoder();
         File authHelloFile = new File(getClass().getResource("/netconfMessages/client_hello_with_auth.xml").getFile());
 
         final List<Object> out = new ArrayList<>();