title: Netty ping pong with a POJO
body: <p>Why doesn't the client receive the POJO which the server sends?</p>
  
  <p>this <a href="http://seeallhearall.blogspot.ca/2012/05/netty-tutorial-part-1-introduction-to.html" rel="nofollow">blog sample</a> is a bit difficult for me to follow.</p>
  
  <p>I realize it's a lot of code, but I don't know how to make slim this down while still using a POJO, in this case <code>Quote</code>, between the client and server.  The server sends a quote when a connection is established:</p>
  
  <pre><code>run:
       [java] Aug 03, 2014 5:32:20 PM net.bounceme.dur.netty.QuoteServerInitializer &lt;init&gt;
       [java] INFO: ..initializing..
       [java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteServerInitializer initChannel
       [java] INFO: ..adding to pipeline..
       [java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteServerHandler &lt;init&gt;
       [java] INFO: ..started..
       [java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteServerHandler channelActive
       [java] INFO: ..sending new server Quote..
       [java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteEncoder encode
       [java] INFO: 
       [java] 
       [java] id      0
       [java] quote   Where there is love there is life.
  ^Cthufir@dur:~/NetBeansProjects/QuoteServer$ 
  thufir@dur:~/NetBeansProjects/QuoteServer$ ^C
  thufir@dur:~/NetBeansProjects/QuoteServer$ 
  </code></pre>
  
  <p>but it never seems to arrive at the client:   </p>
  
  <pre><code>run:
       [java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteClientInitializer &lt;init&gt;
       [java] INFO: ..initializing..
       [java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteClientHandler channelActive
       [java] INFO: ..sending new client Quote..
       [java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteEncoder encode
       [java] INFO: 
       [java] 
       [java] id      0
       [java] quote   client
  ^Cthufir@dur:~/NetBeansProjects/QuoteClient$ 
  thufir@dur:~/NetBeansProjects/QuoteClient$ 
  </code></pre>
  
  <p>Similarly, the quote which the client sends never seems to make it to the server.  Why?</p>
  
  <p>server:</p>
  
  <pre><code>package net.bounceme.dur.netty;
  
  import io.netty.bootstrap.ServerBootstrap;
  import io.netty.channel.EventLoopGroup;
  import io.netty.channel.nio.NioEventLoopGroup;
  import io.netty.channel.socket.nio.NioServerSocketChannel;
  import io.netty.handler.logging.LogLevel;
  import io.netty.handler.logging.LoggingHandler;
  import java.util.logging.Logger;
  
  public final class QuoteServer {
  
      private static final Logger log = Logger.getLogger(QuoteServer.class.getName());
  
      public static void main(String... args) throws InterruptedException {
          MyProps p = new MyProps();
          int port = p.getServerPort();
          new QuoteServer().pingPong(port);
      }
  
      private void pingPong(int port) throws InterruptedException {
          EventLoopGroup bossGroup = new NioEventLoopGroup(1);
          EventLoopGroup workerGroup = new NioEventLoopGroup();
          try {
              ServerBootstrap b = new ServerBootstrap();
              b.group(bossGroup, workerGroup)
                      .channel(NioServerSocketChannel.class)
                      .handler(new LoggingHandler(LogLevel.DEBUG))
                      .childHandler(new QuoteServerInitializer());
              b.bind(port).sync().channel().closeFuture().sync();
          } finally {
              bossGroup.shutdownGracefully();
              workerGroup.shutdownGracefully();
          }
      }
  }
  </code></pre>
  
  <p>server initializer:
      package net.bounceme.dur.netty;</p>
  
  <pre><code>import io.netty.channel.ChannelInitializer;
  import io.netty.channel.ChannelPipeline;
  import io.netty.channel.socket.SocketChannel;
  import io.netty.handler.codec.DelimiterBasedFrameDecoder;
  import io.netty.handler.codec.Delimiters;
  import java.util.logging.Logger;
  
  public class QuoteServerInitializer extends ChannelInitializer&lt;SocketChannel&gt; {
  
      private static final Logger log = Logger.getLogger(QuoteServerInitializer.class.getName());
  
      public QuoteServerInitializer() {
          log.info("..initializing..");
      }
  
      @Override
      public void initChannel(SocketChannel ch) throws Exception {
          log.info("..adding to pipeline..");
          ChannelPipeline pipeline = ch.pipeline();
          pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
          pipeline.addLast(new QuoteDecoder());
          pipeline.addLast(new QuoteEncoder());
          pipeline.addLast(new QuoteServerHandler());
      }
  }
  </code></pre>
  
  <p>server handler:<br>
      package net.bounceme.dur.netty;</p>
  
  <pre><code>import io.netty.channel.ChannelHandlerContext;
  import io.netty.channel.SimpleChannelInboundHandler;
  import java.util.Random;
  import java.util.logging.Logger;
  import net.bounceme.dur.jdbc.Quote;
  
  public class QuoteServerHandler extends SimpleChannelInboundHandler&lt;Quote&gt; {
  
      private static final Logger log = Logger.getLogger(QuoteServerHandler.class.getName());
      private static final Random random = new Random();
  
      public QuoteServerHandler() {
          log.info("..started..");
      }
  
      // Quotes from Mohandas K. Gandhi:
      private static final String[] quotes = {
          "Where there is love there is life.",
          "First they ignore you, then they laugh at you, then they fight you, then you win.",
          "Be the change you want to see in the world.",
          "The weak can never forgive. Forgiveness is the attribute of the strong.",};
  
      private static Quote nextQuote() {
          int quoteId;
          synchronized (random) {
              quoteId = random.nextInt(quotes.length);
          }
          return new Quote(quotes[quoteId]);
      }
  
      @Override
      public void channelActive(ChannelHandlerContext ctx) throws Exception {
          log.info("..sending new server Quote..");
          ctx.writeAndFlush(nextQuote());
      }
  
      @Override
      protected void channelRead0(ChannelHandlerContext chc, Quote quote) throws Exception {
          log.info(quote.toString());
          chc.writeAndFlush(nextQuote());
      }
  
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) {
          log.info(msg.toString());
          ctx.writeAndFlush(nextQuote());
      }
  
      @Override
      public void channelReadComplete(ChannelHandlerContext ctx) {
          ctx.flush();
      }
  
  }
  </code></pre>
  
  <p>client:
      package net.bounceme.dur.netty;</p>
  
  <pre><code>import io.netty.bootstrap.Bootstrap;
  import io.netty.channel.ChannelFuture;
  import io.netty.channel.EventLoopGroup;
  import io.netty.channel.nio.NioEventLoopGroup;
  import io.netty.channel.socket.nio.NioSocketChannel;
  import java.io.IOException;
  import java.util.logging.Logger;
  
  public final class QuoteClient {
  
      private static final Logger log = Logger.getLogger(QuoteClient.class.getName());
  
      public static void main(String... args) throws InterruptedException, IOException {
          new QuoteClient().connect();
      }
  
      public void connect() throws InterruptedException, IOException {
          MyProps p = new MyProps();
          String host = p.getHost();
          int port = p.getServerPort();
          pingPong(host, port);
      }
  
      public void pingPong(String host, int port) throws InterruptedException, IOException {
          EventLoopGroup group = new NioEventLoopGroup();
          try {
              Bootstrap b = new Bootstrap();
              b.group(group)
                      .channel(NioSocketChannel.class)
                      .handler(new QuoteClientInitializer());
              ChannelFuture cf = b.connect(host, port);
              cf.sync().channel().closeFuture().sync();
          } finally {
              group.shutdownGracefully();
          }
      }
  }
  </code></pre>
  
  <p>client initializer:
      package net.bounceme.dur.netty;</p>
  
  <pre><code>import io.netty.channel.ChannelInitializer;
  import io.netty.channel.ChannelPipeline;
  import io.netty.channel.socket.SocketChannel;
  import io.netty.handler.codec.DelimiterBasedFrameDecoder;
  import io.netty.handler.codec.Delimiters;
  import java.util.logging.Logger;
  
  public class QuoteClientInitializer extends ChannelInitializer&lt;SocketChannel&gt; {
  
      private static final Logger log = Logger.getLogger(QuoteClientInitializer.class.getName());
  
      public QuoteClientInitializer() {
          log.info("..initializing..");
      }
  
      @Override
      public void initChannel(SocketChannel ch) {
          ChannelPipeline pipeline = ch.pipeline();
          pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
          pipeline.addLast(new QuoteDecoder());
          pipeline.addLast(new QuoteEncoder());
          pipeline.addLast(new QuoteClientHandler());
      }
  }
  </code></pre>
  
  <p>client handler:
      package net.bounceme.dur.netty;</p>
  
  <pre><code>import io.netty.channel.ChannelHandler.Sharable;
  import io.netty.channel.ChannelHandlerContext;
  import io.netty.channel.SimpleChannelInboundHandler;
  import java.util.logging.Logger;
  import net.bounceme.dur.jdbc.Quote;
  
  @Sharable
  public class QuoteClientHandler extends SimpleChannelInboundHandler&lt;Quote&gt; {
  
      private static final Logger log = Logger.getLogger(QuoteClient.class.getName());
  
      @Override
      public void channelActive(ChannelHandlerContext ctx) throws Exception {
          log.info("..sending new client Quote..");
          ctx.writeAndFlush(new Quote("client"));
      }
  
      @Override
      protected void channelRead0(ChannelHandlerContext chc, Quote quote) throws Exception {
          log.info(quote.toString());
      }
  
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) {
          log.info(msg.toString());
          ctx.writeAndFlush(new Quote("client"));
      }
  
      @Override
      public void channelReadComplete(ChannelHandlerContext ctx) {
          ctx.fireChannelReadComplete();
      }
  
      @Override
      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
          log.info(cause.toString());
          ctx.close();
      }
  
  }
  </code></pre>
  
  <p>decoder:
      package net.bounceme.dur.netty;</p>
  
  <pre><code>import io.netty.channel.ChannelHandlerContext;
  import io.netty.handler.codec.MessageToMessageDecoder;
  import java.util.List;
  import java.util.logging.Logger;
  import net.bounceme.dur.jdbc.Quote;
  
  public class QuoteDecoder extends MessageToMessageDecoder&lt;Quote&gt; {
  
      private static final Logger log = Logger.getLogger(QuoteDecoder.class.getName());
  
      @Override
      protected void decode(ChannelHandlerContext chc, Quote quote, List&lt;Object&gt; list) throws Exception {
          log.info(quote.toString());
          list.add(quote);
      }
  }
  </code></pre>
  
  <p>encoder:
      package net.bounceme.dur.netty;</p>
  
  <pre><code>import io.netty.channel.ChannelHandlerContext;
  import io.netty.handler.codec.MessageToMessageEncoder;
  import java.util.List;
  import java.util.logging.Logger;
  import net.bounceme.dur.jdbc.Quote;
  
  public class QuoteEncoder extends MessageToMessageEncoder&lt;Quote&gt; {
  
              private static final Logger log = Logger.getLogger(QuoteEncoder.class.getName());
  
  
      @Override
      protected void encode(ChannelHandlerContext chc, Quote quote, List&lt;Object&gt; list) throws Exception {
          log.info(quote.toString());
          list.add(quote);
      }
  }
  </code></pre>
  
  <p>It's quite notable that the en/de-code methods never log to the console.</p>