org.r358.poolnetty.test.MultiConnectionTest.java Source code

Java tutorial

Introduction

Here is the source code for org.r358.poolnetty.test.MultiConnectionTest.java

Source

/*
 * Copyright (c) 2014 R358 https://github.com/R358
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package org.r358.poolnetty.test;

import org.r358.poolnetty.common.*;
import org.r358.poolnetty.pool.NettyConnectionPool;
import org.r358.poolnetty.pool.NettyConnectionPoolBuilder;
import org.r358.poolnetty.pool.reaper.FullPassSimpleLeaseReaper;
import org.r358.poolnetty.test.simpleserver.SimpleInboundHandler;
import org.r358.poolnetty.test.simpleserver.SimpleOutboundHandler;
import org.r358.poolnetty.test.simpleserver.SimpleServer;
import org.r358.poolnetty.test.simpleserver.SimpleServerListener;
import org.r358.poolnetty.test.simpleserver.util.TestPoolProviderListener;
import org.r358.poolnetty.test.simpleserver.util.TestUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 *
 */
@RunWith(JUnit4.class)
public class MultiConnectionTest {

    /**
     * Test the creation and leasing of connections and harvesting of expired leases.
     *
     * @throws Exception
     */
    @Test
    public void testHarvestingWithMultiples() throws Exception {

        final int maxImmortal = 5;
        final int maxEphemeral = 10;
        final int maxAll = maxEphemeral + maxImmortal;

        TestPoolProviderListener ppl = new TestPoolProviderListener();
        final ArrayList<Object> serverReceivedMessages = new ArrayList<>();
        String testMessage = "The cat sat on the mat.";

        //
        // The simple server side for testing.
        //

        SimpleServer simpleServer = new SimpleServer("127.0.0.1", 1887, 10, new SimpleServerListener() {

            @Override
            public void newConnection(ChannelHandlerContext ctx) {

            }

            @Override
            public void newValue(ChannelHandlerContext ctx, String val) {
                serverReceivedMessages.add(val);
                ctx.writeAndFlush(val);
            }
        });

        simpleServer.start();

        final CountDownLatch leaseExpiredHandlerCalled = new CountDownLatch(1);

        //
        // Build the pool.
        //

        NettyConnectionPoolBuilder ncb = new NettyConnectionPoolBuilder();
        ncb.withImmortalCount(maxImmortal);
        ncb.withMaxEphemeralCount(maxEphemeral);
        ncb.withEphemeralLifespanMillis(5000);
        ncb.withReaperIntervalMillis(1000); // Set short for testing..
        ncb.withLeaseExpiryHarvester(new FullPassSimpleLeaseReaper());
        ncb.withLeaseExpiredHandler(new LeaseExpiredHandler() {
            @Override
            public boolean closeExpiredLease(LeasedContext context, PoolProvider provider) {
                leaseExpiredHandlerCalled.countDown();
                return true; // Cause lease to be expired.
            }
        });

        final EventLoopGroup elg = new NioEventLoopGroup();

        //
        // Create the boot strap.
        //
        ncb.withBootstrapProvider(new BootstrapProvider() {
            @Override
            public Bootstrap createBootstrap(PoolProvider poolProvider) {
                Bootstrap bs = new Bootstrap();
                bs.group(elg);
                bs.channel(NioSocketChannel.class);
                bs.option(ChannelOption.SO_KEEPALIVE, true);
                bs.option(ChannelOption.AUTO_READ, true);
                return bs;
            }
        });

        //
        // Sets up the connection info and the channel initializer.
        //
        ncb.withConnectionInfoProvider(new ConnectionInfoProvider() {
            @Override
            public ConnectionInfo connectionInfo(PoolProvider poolProvider) {

                return new ConnectionInfo(new InetSocketAddress("127.0.0.1", 1887), null, new ChannelInitializer() {
                    @Override
                    protected void initChannel(Channel ch) throws Exception {
                        ch.pipeline().addLast("decode", new SimpleInboundHandler(10));
                        ch.pipeline().addLast("encode", new SimpleOutboundHandler(10));
                    }
                });

            }
        });

        //
        // Make the pool add listener and start.
        //
        NettyConnectionPool ncp = ncb.build();
        ncp.addListener(ppl);

        final CountDownLatch leaseAll = new CountDownLatch(maxAll);

        final CountDownLatch harvestedLeases = new CountDownLatch(maxAll);

        final CountDownLatch closedConnections = new CountDownLatch(maxEphemeral);

        final CountDownLatch openedConnections = new CountDownLatch(maxAll + maxImmortal);

        ncp.addListener(new PoolProviderListenerAdapter() {

            @Override
            public void connectionCreated(PoolProvider provider, Channel channel, boolean immortal) {
                openedConnections.countDown();
            }

            @Override
            public void leaseGranted(PoolProvider provider, Channel channel, Object userObject) {
                leaseAll.countDown();
            }

            @Override
            public void leaseExpired(PoolProvider provider, Channel channel, Object userObject) {
                harvestedLeases.countDown();
            }

            @Override
            public void connectionClosed(PoolProvider provider, Channel channel) {
                closedConnections.countDown();
            }
        });

        TestCase.assertTrue(ncp.start(10, TimeUnit.SECONDS));

        TestCase.assertEquals(5, ((List) TestUtil.getField(ncp, "immortalContexts")).size());
        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "ephemeralContexts")).size());

        //
        // Lease all 15 connections which is 5 immortal and 10 ephemeral.
        //

        List<LeasedChannel> leasedChannels = new ArrayList<>();

        for (int t = 0; t < 15; t++) {
            leasedChannels.add(ncp.lease(2, TimeUnit.SECONDS, t));
        }

        TestCase.assertTrue(leaseAll.await(5, TimeUnit.SECONDS));

        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "immortalContexts")).size());
        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "ephemeralContexts")).size());

        TestCase.assertEquals(maxAll, ((List) TestUtil.getField(ncp, "leasedContexts")).size());
        TestCase.assertEquals(maxAll, ((Set) TestUtil.getField(ncp, "leasedContextSet")).size());

        //
        // Over this period the leases should all expire and drop back to the pool.
        //

        TestCase.assertTrue(harvestedLeases.await(10, TimeUnit.SECONDS));

        //
        // Wait for ephemeral connections to be closed.
        //

        TestCase.assertTrue(closedConnections.await(10, TimeUnit.SECONDS));

        //
        // At this point we wait until the replace immortal connections are created.
        //

        // Total opened connections should be maxAll+maxImmortal.
        TestCase.assertTrue(openedConnections.await(10, TimeUnit.SECONDS));

        TestCase.assertEquals(5, ((List) TestUtil.getField(ncp, "immortalContexts")).size());

        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "ephemeralContexts")).size());

        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "leasedContexts")).size());
        TestCase.assertEquals(0, ((Set) TestUtil.getField(ncp, "leasedContextSet")).size());

        simpleServer.stop();

    }

    @Test
    public void leaseAndYield() throws Exception {
        final int maxImmortal = 5;
        final int maxEphemeral = 10;
        final int maxAll = maxEphemeral + maxImmortal;

        TestPoolProviderListener ppl = new TestPoolProviderListener();
        final ArrayList<Object> serverReceivedMessages = new ArrayList<>();
        String testMessage = "The cat sat on the mat.";

        //
        // The simple server side for testing.
        //

        SimpleServer simpleServer = new SimpleServer("127.0.0.1", 1887, 10, new SimpleServerListener() {

            @Override
            public void newConnection(ChannelHandlerContext ctx) {

            }

            @Override
            public void newValue(ChannelHandlerContext ctx, String val) {
                serverReceivedMessages.add(val);
                ctx.writeAndFlush(val);
            }
        });

        simpleServer.start();

        final CountDownLatch leaseExpiredHandlerCalled = new CountDownLatch(1);

        //
        // Build the pool.
        //

        NettyConnectionPoolBuilder ncb = new NettyConnectionPoolBuilder();
        ncb.withImmortalCount(maxImmortal);
        ncb.withMaxEphemeralCount(maxEphemeral);
        ncb.withEphemeralLifespanMillis(5000);
        ncb.withReaperIntervalMillis(1000); // Set short for testing..
        ncb.withLeaseExpiryHarvester(new FullPassSimpleLeaseReaper());
        ncb.withLeaseExpiredHandler(new LeaseExpiredHandler() {
            @Override
            public boolean closeExpiredLease(LeasedContext context, PoolProvider provider) {
                leaseExpiredHandlerCalled.countDown();
                return true; // Cause lease to be expired.
            }
        });

        final EventLoopGroup elg = new NioEventLoopGroup();

        //
        // Create the boot strap.
        //
        ncb.withBootstrapProvider(new BootstrapProvider() {
            @Override
            public Bootstrap createBootstrap(PoolProvider poolProvider) {
                Bootstrap bs = new Bootstrap();
                bs.group(elg);
                bs.channel(NioSocketChannel.class);
                bs.option(ChannelOption.SO_KEEPALIVE, true);
                bs.option(ChannelOption.AUTO_READ, true);
                return bs;
            }
        });

        //
        // Sets up the connection info and the channel initializer.
        //
        ncb.withConnectionInfoProvider(new ConnectionInfoProvider() {
            @Override
            public ConnectionInfo connectionInfo(PoolProvider poolProvider) {

                return new ConnectionInfo(new InetSocketAddress("127.0.0.1", 1887), null, new ChannelInitializer() {
                    @Override
                    protected void initChannel(Channel ch) throws Exception {
                        ch.pipeline().addLast("decode", new SimpleInboundHandler(10));
                        ch.pipeline().addLast("encode", new SimpleOutboundHandler(10));
                    }
                });

            }
        });

        //
        // Make the pool add listener and start.
        //
        NettyConnectionPool ncp = ncb.build();
        ncp.addListener(ppl);

        final CountDownLatch leaseAll = new CountDownLatch(maxAll);

        final CountDownLatch closedConnections = new CountDownLatch(maxEphemeral);

        final CountDownLatch openedConnections = new CountDownLatch(maxAll);

        final CountDownLatch yieldedConnections = new CountDownLatch(maxAll);

        ncp.addListener(new PoolProviderListenerAdapter() {

            @Override
            public void connectionCreated(PoolProvider provider, Channel channel, boolean immortal) {
                openedConnections.countDown();
            }

            @Override
            public void leaseGranted(PoolProvider provider, Channel channel, Object userObject) {
                leaseAll.countDown();
            }

            @Override
            public void leaseYield(PoolProvider provider, Channel channel, Object userObject) {
                yieldedConnections.countDown();
            }

            @Override
            public void connectionClosed(PoolProvider provider, Channel channel) {
                closedConnections.countDown();
            }
        });

        TestCase.assertTrue(ncp.start(10, TimeUnit.SECONDS));

        TestCase.assertEquals(5, ((List) TestUtil.getField(ncp, "immortalContexts")).size());
        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "ephemeralContexts")).size());

        //
        // Lease all 15 connections which is 5 immortal and 10 ephemeral.
        //

        List<LeasedChannel> leasedChannels = new ArrayList<>();

        for (int t = 0; t < maxAll; t++) {
            leasedChannels.add(ncp.lease(2, TimeUnit.SECONDS, t));
        }

        TestCase.assertTrue(leaseAll.await(5, TimeUnit.SECONDS));

        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "immortalContexts")).size());
        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "ephemeralContexts")).size());

        TestCase.assertEquals(maxAll, ((List) TestUtil.getField(ncp, "leasedContexts")).size());
        TestCase.assertEquals(maxAll, ((Set) TestUtil.getField(ncp, "leasedContextSet")).size());

        while (!leasedChannels.isEmpty()) {
            leasedChannels.remove(0).yield();
        }

        TestCase.assertTrue(yieldedConnections.await(10, TimeUnit.SECONDS));

        //
        // At this point we have yielded the leases so we need to wait for
        // the ephemeral connections to be closed.
        //

        //
        // Wait for ephemeral connections to be closed.
        //

        TestCase.assertTrue(closedConnections.await(20, TimeUnit.SECONDS));

        //
        // At this point we wait until the replace immortal connections are created.
        //

        // Total opened connections should be maxAll.
        TestCase.assertTrue(openedConnections.await(10, TimeUnit.SECONDS));

        TestCase.assertEquals(5, ((List) TestUtil.getField(ncp, "immortalContexts")).size());

        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "ephemeralContexts")).size());

        TestCase.assertEquals(0, ((List) TestUtil.getField(ncp, "leasedContexts")).size());
        TestCase.assertEquals(0, ((Set) TestUtil.getField(ncp, "leasedContextSet")).size());

        simpleServer.stop();
    }

}