pl.coffeepower.blog.messagebus.MessageBusTestHelper.java Source code

Java tutorial

Introduction

Here is the source code for pl.coffeepower.blog.messagebus.MessageBusTestHelper.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Micha Jonko
 *
 * 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 pl.coffeepower.blog.messagebus;

import com.google.common.base.MoreObjects;
import com.google.common.base.Stopwatch;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.Longs;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Stage;

import lombok.Getter;
import lombok.Value;

import org.agrona.concurrent.IdleStrategy;
import org.agrona.concurrent.SleepingIdleStrategy;
import org.gridkit.nanocloud.Cloud;
import org.gridkit.nanocloud.CloudFactory;
import org.gridkit.vicluster.ViProps;
import org.gridkit.vicluster.telecontrol.jvm.JvmProps;
import org.junit.After;
import org.junit.Before;

import pl.coffeepower.blog.messagebus.Configuration.Const;
import pl.coffeepower.blog.messagebus.util.BytesEventModule;

import java.io.Serializable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.LongStream;

abstract class MessageBusTestHelper implements Serializable {

    static final String NODE_SUBSCRIBER_1 = "sub-1";
    static final String NODE_SUBSCRIBER_2 = "sub-2";
    static final String NODE_SUBSCRIBER_3 = "sub-3";
    static final String NODE_PUBLISHER = "pub";
    private static final long serialVersionUID = -5525765906174658715L;
    @Getter
    private Cloud cloud;

    @Before
    public void setUp() throws Exception {
        cloud = CloudFactory.createCloud();
        ViProps.at(cloud.node("**")).setLocalType();
        JvmProps.at(cloud.node("**")).addJvmArgs("-Xms128m", "-Xmx128m");
        cloud.node(NODE_SUBSCRIBER_1).setProp(Const.SUBSCRIBER_NAME_KEY, "SUB-1");
        cloud.node(NODE_SUBSCRIBER_2).setProp(Const.SUBSCRIBER_NAME_KEY, "SUB-2");
        cloud.node(NODE_SUBSCRIBER_3).setProp(Const.SUBSCRIBER_NAME_KEY, "SUB-3");
        cloud.nodes(NODE_PUBLISHER, NODE_SUBSCRIBER_1, NODE_SUBSCRIBER_2, NODE_SUBSCRIBER_3).touch();
    }

    @After
    public void tearDown() throws Exception {
        cloud.shutdown();
    }

    Publisher executePublisher(final Engine engine) throws InterruptedException {
        Publisher publisher = Guice.createInjector(Stage.PRODUCTION, new TestConfigurationModule(),
                new BytesEventModule(), engine.getModule()).getInstance(Publisher.class);
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            Fixtures fixtures = new Fixtures();
            Stopwatch stopwatch = Stopwatch.createStarted();
            LongStream.rangeClosed(fixtures.getFirstMessageId(), fixtures.getNumberOfMessages()).forEach(value -> {
                IdleStrategy idleStrategy = new SleepingIdleStrategy(TimeUnit.MICROSECONDS.toNanos(1L));
                while (!publisher.send(Bytes.concat(Longs.toByteArray(value), fixtures.getAdditionalData()))) {
                    idleStrategy.idle();
                }
            });
            System.out.println("Sent all messages in " + stopwatch.stop());
        });
        executorService.shutdown();
        executorService.awaitTermination(1L, TimeUnit.MINUTES);
        return publisher;
    }

    Future<Boolean> createSubscriberFuture(final Engine engine) {
        return createSubscriberFuture(NODE_SUBSCRIBER_1, engine);
    }

    Future<Boolean> createSubscriberFuture(final String cloudNode, final Engine engine) {
        return cloud.node(cloudNode).submit((Callable<Boolean> & Serializable) () -> {
            Fixtures fixtures = new Fixtures();
            AtomicBoolean state = new AtomicBoolean(true);
            AtomicLong lastReceived = new AtomicLong();
            AtomicLong messagesCounter = new AtomicLong();
            Subscriber subscriber = Guice.createInjector(Stage.PRODUCTION, new TestConfigurationModule(),
                    new BytesEventModule(), engine.getModule()).getInstance(Subscriber.class);
            subscriber.register((data, length) -> {
                messagesCounter.incrementAndGet();
                if (state.get()) {
                    long prevReceivedValue = lastReceived.getAndSet(Longs.fromByteArray(data));
                    long lastReceivedValue = lastReceived.get();
                    if (lastReceivedValue != (prevReceivedValue + 1)
                            || data[length - 1] != fixtures.getLastAdditionalDataByte()) {
                        state.set(false);
                    }
                }
            });
            IdleStrategy idleStrategy = new SleepingIdleStrategy(TimeUnit.MICROSECONDS.toNanos(1L));
            while (state.get() && lastReceived.get() < fixtures.getNumberOfMessages()) {
                idleStrategy.idle();
            }
            subscriber.close();
            if (!state.get()) {
                System.out.println("Broken connection on messageId=" + lastReceived.get());
                System.out.println("Last messageId=" + lastReceived);
            }
            return state.get();
        });
    }

    private static final class TestConfigurationModule extends AbstractModule {

        @Override
        protected void configure() {
            bind(Configuration.class).to(TestConfiguration.class).asEagerSingleton();
        }
    }

    private static final class TestConfiguration implements Configuration {

        @Override
        public String getMulticastAddress() {
            return "225.0.0.11";
        }

        @Override
        public int getMulticastPort() {
            return 12345;
        }

        @Override
        public String getBindAddress() {
            return "127.0.0.1";
        }

        @Override
        public int getTopicId() {
            return 1;
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this).add("multicastAddress", getMulticastAddress())
                    .add("multicastPort", getMulticastPort()).add("bindAddress", getBindAddress())
                    .add("topicId", getTopicId()).toString();
        }
    }

    @Value
    private static final class Fixtures implements Serializable {
        long firstMessageId = 1L;
        long numberOfMessages = 100_000L;
        byte[] additionalData = "9876543210123456789qazxswedcvfrtgbnhyujm".getBytes();
        int additionalDataLength = additionalData.length;
        byte lastAdditionalDataByte = additionalData[additionalDataLength - 1];
    }
}