package com.spotify.netty4.handler.codec.zmtp; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalEventLoopGroup; import io.netty.channel.local.LocalServerChannel; import io.netty.util.ReferenceCountUtil; /** * Tests the behaviours of ChannelPipelines, using the local transport method. * A PipelineTester instance has two ends named server and client where the server is the * position furthest upstream in the pipeline, and client is outside of the pipeline reading from * and writing to it. */ class PipelineTester { private final BlockingQueue<ByteBuf> emittedOutside = new LinkedBlockingQueue<ByteBuf>(); private final BlockingQueue<Object> emittedInside = new LinkedBlockingQueue<Object>(); private Channel outerChannel = null; private Channel innerChannel = null; private static final AtomicInteger port = new AtomicInteger(); /** * Constructs a server using pipeline and a client communicating with it. * * @param handlers Server channel handlers. */ public PipelineTester(final ChannelHandler... handlers) { final LocalAddress address = new LocalAddress("pipeline-tester-" + port.incrementAndGet()); final ServerBootstrap sb = new ServerBootstrap(); sb.group(new LocalEventLoopGroup(1), new LocalEventLoopGroup()); sb.channel(LocalServerChannel.class); sb.childHandler(new ChannelInitializer<LocalChannel>() { @Override protected void initChannel(final LocalChannel ch) throws Exception { ch.pipeline().addLast(handlers); ch.pipeline().addLast("pipelineTesterEndpoint", new ChannelInboundHandlerAdapter() { @Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { ReferenceCountUtil.releaseLater(msg); emittedInside.put(msg); } @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { innerChannel = ctx.channel(); } }); } }); sb.bind(address).awaitUninterruptibly(); final Bootstrap cb = new Bootstrap(); cb.group(new LocalEventLoopGroup()); cb.channel(LocalChannel.class); cb.handler(new ChannelInitializer<LocalChannel>() { @Override protected void initChannel(final LocalChannel ch) throws Exception { ch.pipeline().addLast("1", new ChannelInboundHandlerAdapter() { @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { outerChannel = ctx.channel(); } @Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { ReferenceCountUtil.releaseLater(msg); emittedOutside.put((ByteBuf) msg); } }); } }); cb.connect(address).awaitUninterruptibly(); } /** * Read the ChannelBuffers emitted from this pipeline in the client end. * * @return a ByteBuf from the the client FIFO */ public ByteBuf readClient() { try { return emittedOutside.take(); } catch (InterruptedException e) { throw new Error(e); } } /** * Write a ByteBuf to the pipeline from the client end. * * @param buf the ByteBuf to write */ public void writeClient(ByteBuf buf) { outerChannel.writeAndFlush(buf); } /** * Read an Object from the server end of the pipeline. This can be a ByteBuf, or, if * there is a FrameDecoder some sort of pojo. * * @return an Object read from the server end. */ public Object readServer() { try { return emittedInside.take(); } catch (InterruptedException e) { throw new Error(e); } } /** * Write a Object to the server end of the pipeline. * * @param message the Object to be written */ public void writeServer(Object message) { innerChannel.writeAndFlush(message); } }