Merge changes Icd18348e,I0e642b1b,I304bec5e
authorTony Tkacik <ttkacik@cisco.com>
Thu, 18 Dec 2014 14:20:29 +0000 (14:20 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 18 Dec 2014 14:20:29 +0000 (14:20 +0000)
* changes:
  BUG-2459: maintain a cache of grammar caches
  BUG-2459: cache GrammarCache
  BUG-2459: centralize codec allocation

opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java
opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionTest.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSession.java
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXICodec.java
opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionTest.java

index 9bafe9760ae202cd8c75adfd0aa8a27aafae42ae..31a7661b2c17ccb8fdff3688f7111a26b4fe3f97 100644 (file)
@@ -9,11 +9,11 @@
 package org.opendaylight.controller.netconf.client;
 
 import io.netty.channel.Channel;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.codec.MessageToByteEncoder;
 import java.util.Collection;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.nettyutil.AbstractNetconfSession;
-import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec;
-import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder;
-import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToEXIEncoder;
 import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToXMLEncoder;
 import org.opendaylight.controller.netconf.nettyutil.handler.NetconfXMLToMessageDecoder;
 import org.slf4j.Logger;
@@ -49,10 +49,10 @@ public class NetconfClientSession extends AbstractNetconfSession<NetconfClientSe
     }
 
     @Override
-    protected void addExiHandlers(final NetconfEXICodec exiCodec) {
+    protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder<NetconfMessage> encoder) {
         // TODO used only in negotiator, client supports only auto start-exi
-        replaceMessageDecoder(new NetconfEXIToMessageDecoder(exiCodec));
-        replaceMessageEncoder(new NetconfMessageToEXIEncoder(exiCodec));
+        replaceMessageDecoder(decoder);
+        replaceMessageEncoder(encoder);
     }
 
     @Override
index 850ad55fc61314481cde821ff575bee9789a3f86..e11be554d0df948700dc37b8650db0e3c5722172 100644 (file)
@@ -12,7 +12,6 @@ import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
-
 import com.google.common.collect.Lists;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandler;
@@ -24,6 +23,8 @@ import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec;
+import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder;
+import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToEXIEncoder;
 import org.openexi.proc.common.EXIOptions;
 
 public class NetconfClientSessionTest {
@@ -53,7 +54,9 @@ public class NetconfClientSessionTest {
         Mockito.doReturn("").when(channelHandler).toString();
 
         NetconfClientSession session = new NetconfClientSession(sessionListener, channel, sessId, caps);
-        session.addExiHandlers(codec);
+        final NetconfMessageToEXIEncoder exiEncoder = new NetconfMessageToEXIEncoder(codec);
+        final NetconfEXIToMessageDecoder exiDecoder = new NetconfEXIToMessageDecoder(codec);
+        session.addExiHandlers(exiDecoder, exiEncoder);
         session.stopExiCommunication();
 
         assertEquals(caps, session.getServerCapabilities());
index ca604a4f6531c4f03d878825ac5fde5cfd360aeb..8f2c39df063a813696735daa66d8e7831f9a756c 100644 (file)
@@ -10,15 +10,15 @@ package org.opendaylight.controller.netconf.impl;
 
 import com.google.common.base.Preconditions;
 import io.netty.channel.Channel;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.codec.MessageToByteEncoder;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
 import org.opendaylight.controller.netconf.nettyutil.AbstractNetconfSession;
-import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec;
-import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder;
-import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToEXIEncoder;
 import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToXMLEncoder;
 import org.opendaylight.controller.netconf.nettyutil.handler.NetconfXMLToMessageDecoder;
 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
@@ -46,8 +46,8 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
     private Date loginTime;
     private long inRpcSuccess, inRpcFail, outRpcError;
 
-    public NetconfServerSession(NetconfServerSessionListener sessionListener, Channel channel, long sessionId,
-            NetconfHelloMessageAdditionalHeader header) {
+    public NetconfServerSession(final NetconfServerSessionListener sessionListener, final Channel channel, final long sessionId,
+            final NetconfHelloMessageAdditionalHeader header) {
         super(sessionListener, channel, sessionId);
         this.header = header;
         LOG.debug("Session {} created", toString());
@@ -109,7 +109,7 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
         return builder.build();
     }
 
-    private Class<? extends Transport> getTransportForString(String transport) {
+    private Class<? extends Transport> getTransportForString(final String transport) {
         switch(transport) {
         case "ssh" :
             return NetconfSsh.class;
@@ -120,7 +120,7 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
         }
     }
 
-    private String formatDateTime(Date loginTime) {
+    private String formatDateTime(final Date loginTime) {
         SimpleDateFormat dateFormat = new SimpleDateFormat(ISO_DATE_FORMAT);
         return dateFormat.format(loginTime);
     }
@@ -131,9 +131,9 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
     }
 
     @Override
-    protected void addExiHandlers(NetconfEXICodec exiCodec) {
-        replaceMessageDecoder(new NetconfEXIToMessageDecoder(exiCodec));
-        replaceMessageEncoderAfterNextMessage(new NetconfMessageToEXIEncoder(exiCodec));
+    protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder<NetconfMessage> encoder) {
+        replaceMessageDecoder(decoder);
+        replaceMessageEncoderAfterNextMessage(encoder);
     }
 
     @Override
index fd11ce8c51875e379976917fd722ce6c5797d0ec..8eb792b6cdfc368930db066dec991abdf47a3ea8 100644 (file)
@@ -10,6 +10,8 @@ package org.opendaylight.controller.netconf.nettyutil;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelHandler;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.codec.MessageToByteEncoder;
 import java.io.IOException;
 import org.opendaylight.controller.netconf.api.NetconfExiSession;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
@@ -17,6 +19,8 @@ import org.opendaylight.controller.netconf.api.NetconfSession;
 import org.opendaylight.controller.netconf.api.NetconfSessionListener;
 import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
 import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec;
+import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder;
+import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToEXIEncoder;
 import org.opendaylight.controller.netconf.nettyutil.handler.exi.EXIParameters;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.protocol.framework.AbstractProtocolSession;
@@ -117,12 +121,22 @@ public abstract class AbstractNetconfSession<S extends NetconfSession, L extends
             LOG.warn("Unable to parse EXI parameters from {} on session {}", startExiMessage, this, e);
             throw new IllegalArgumentException("Cannot parse options", e);
         }
+
         final NetconfEXICodec exiCodec = new NetconfEXICodec(exiParams.getOptions());
-        addExiHandlers(exiCodec);
+        final NetconfMessageToEXIEncoder exiEncoder = new NetconfMessageToEXIEncoder(exiCodec);
+        final NetconfEXIToMessageDecoder exiDecoder = new NetconfEXIToMessageDecoder(exiCodec);
+        addExiHandlers(exiDecoder, exiEncoder);
+
         LOG.debug("Session {} EXI handlers added to pipeline", this);
     }
 
-    protected abstract void addExiHandlers(NetconfEXICodec exiCodec);
+    /**
+     * Add a set encoder/decoder tuple into the channel pipeline as appropriate.
+     *
+     * @param decoder EXI decoder
+     * @param encoder EXI encoder
+     */
+    protected abstract void addExiHandlers(ByteToMessageDecoder decoder, MessageToByteEncoder<NetconfMessage> encoder);
 
     public final boolean isUp() {
         return up;
index 30867bcd186390ff6d73c737a02d457c60955f92..16da7a7f9dcf3eabb7fee06e8c3201fce1c41155 100644 (file)
@@ -1,6 +1,9 @@
 package org.opendaylight.controller.netconf.nettyutil.handler;
 
 import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import org.openexi.proc.HeaderOptionsOutputType;
 import org.openexi.proc.common.EXIOptions;
 import org.openexi.proc.common.EXIOptionsException;
@@ -30,13 +33,30 @@ public final class NetconfEXICodec {
         }
     };
 
+    /**
+     * Since we have a limited number of options we can have, instantiating a weak cache
+     * will allow us to reuse instances where possible.
+     */
+    private static final LoadingCache<Short, GrammarCache> GRAMMAR_CACHES = CacheBuilder.newBuilder().weakValues().build(new CacheLoader<Short, GrammarCache>() {
+        @Override
+        public GrammarCache load(final Short key) {
+            return new GrammarCache(key);
+        }
+    });
+
+    /**
+     * Grammar cache acts as a template and is duplicated by the Transmogrifier and the Reader
+     * before use. It is safe to reuse a single instance.
+     */
+    private final GrammarCache exiGrammarCache;
     private final EXIOptions exiOptions;
 
     public NetconfEXICodec(final EXIOptions exiOptions) {
         this.exiOptions = Preconditions.checkNotNull(exiOptions);
+        this.exiGrammarCache = createGrammarCache(exiOptions);
     }
 
-    private GrammarCache getGrammarCache() {
+    private static GrammarCache createGrammarCache(final EXIOptions exiOptions) {
         short go = GrammarOptions.DEFAULT_OPTIONS;
         if (exiOptions.getPreserveComments()) {
             go = GrammarOptions.addCM(go);
@@ -51,13 +71,13 @@ public final class NetconfEXICodec {
             go = GrammarOptions.addPI(go);
         }
 
-        return new GrammarCache(null, go);
+        return GRAMMAR_CACHES.getUnchecked(go);
     }
 
     EXIReader getReader() throws EXIOptionsException {
         final EXIReader r = new EXIReader();
         r.setPreserveLexicalValues(exiOptions.getPreserveLexicalValues());
-        r.setGrammarCache(getGrammarCache());
+        r.setGrammarCache(exiGrammarCache);
         r.setEntityResolver(ENTITY_RESOLVER);
         return r;
     }
@@ -66,7 +86,7 @@ public final class NetconfEXICodec {
         final Transmogrifier transmogrifier = new Transmogrifier();
         transmogrifier.setAlignmentType(exiOptions.getAlignmentType());
         transmogrifier.setBlockSize(exiOptions.getBlockSize());
-        transmogrifier.setGrammarCache(getGrammarCache());
+        transmogrifier.setGrammarCache(exiGrammarCache);
         transmogrifier.setOutputCookie(OUTPUT_EXI_COOKIE);
         transmogrifier.setOutputOptions(HeaderOptionsOutputType.all);
         transmogrifier.setResolveExternalGeneralEntities(false);
index 7946afdbf5e1c80add226b08137b1faedd3bf6bf..3a1b26dd9e4dc0c44fedc890a78b9586c6512e29 100644 (file)
@@ -19,12 +19,13 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
-
 import com.google.common.base.Optional;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.codec.MessageToByteEncoder;
 import java.util.Collections;
 import org.junit.Before;
 import org.junit.Test;
@@ -34,7 +35,6 @@ import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.NetconfSession;
 import org.opendaylight.controller.netconf.api.NetconfSessionListener;
 import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
-import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec;
 import org.opendaylight.controller.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
@@ -115,7 +115,7 @@ public class AbstractNetconfSessionTest {
         testingNetconfSession = spy(testingNetconfSession);
 
         testingNetconfSession.startExiCommunication(NetconfStartExiMessage.create(new EXIOptions(), "4"));
-        verify(testingNetconfSession).addExiHandlers(any(NetconfEXICodec.class));
+        verify(testingNetconfSession).addExiHandlers(any(ByteToMessageDecoder.class), any(MessageToByteEncoder.class));
     }
 
     @Test
@@ -148,7 +148,7 @@ public class AbstractNetconfSessionTest {
         }
 
         @Override
-        protected void addExiHandlers(final NetconfEXICodec exiCodec) {}
+        protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder<NetconfMessage> encoder) {}
 
         @Override
         public void stopExiCommunication() {}