reactor.ipc.netty.udp.UdpServerTests.java Source code

Java tutorial

Introduction

Here is the source code for reactor.ipc.netty.udp.UdpServerTests.java

Source

/*
 * Copyright (c) 2011-2016 Pivotal Software Inc, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package reactor.ipc.netty.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import io.netty.channel.ChannelOption;
import io.netty.channel.socket.InternetProtocolFamily;
import io.netty.util.NetUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import reactor.ipc.netty.NettyContext;
import reactor.ipc.netty.SocketUtils;
import reactor.util.Logger;
import reactor.util.Loggers;

import static org.hamcrest.MatcherAssert.assertThat;

/**
 * @author Jon Brisbin
 * @author Stephane Maldini
 */
public class UdpServerTests {

    final Logger log = Loggers.getLogger(getClass());

    ExecutorService threadPool;

    @Before
    public void setup() {
        threadPool = Executors.newCachedThreadPool();
    }

    @After
    public void cleanup() throws InterruptedException {
        threadPool.shutdown();
        threadPool.awaitTermination(5, TimeUnit.SECONDS);
        Schedulers.shutdownNow();
    }

    @Test
    @Ignore
    public void supportsReceivingDatagrams() throws InterruptedException {
        final int port = SocketUtils.findAvailableUdpPort();
        final CountDownLatch latch = new CountDownLatch(4);

        final NettyContext server = UdpClient.create(port).newHandler((in, out) -> {
            in.receive().asByteArray().log().subscribe(bytes -> {
                if (bytes.length == 1024) {
                    latch.countDown();
                }
            });
            return Flux.never();
        }).doOnSuccess(v -> {
            try {
                DatagramChannel udp = DatagramChannel.open();
                udp.configureBlocking(true);
                udp.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));

                byte[] data = new byte[1024];
                new Random().nextBytes(data);
                for (int i = 0; i < 4; i++) {
                    udp.write(ByteBuffer.wrap(data));
                }

                udp.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).block();

        assertThat("latch was counted down", latch.await(10, TimeUnit.SECONDS));
        server.dispose();
    }

    @Test
    @SuppressWarnings("unchecked")
    public void supportsUdpMulticast() throws Exception {
        final int port = SocketUtils.findAvailableUdpPort();
        final CountDownLatch latch = new CountDownLatch(Schedulers.DEFAULT_POOL_SIZE);
        Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();

        final InetAddress multicastGroup = InetAddress.getByName("230.0.0.1");
        final NetworkInterface multicastInterface = findMulticastEnabledIPv4Interface();
        log.info("Using network interface '{}' for multicast", multicastInterface);
        final Collection<NettyContext> servers = new ArrayList<>();

        for (int i = 0; i < 4; i++) {
            NettyContext server = UdpClient.create(opts -> opts.option(ChannelOption.SO_REUSEADDR, true)
                    .connect(port).protocolFamily(InternetProtocolFamily.IPv4)).newHandler((in, out) -> {
                        Flux.<NetworkInterface>generate(s -> {
                            if (ifaces.hasMoreElements()) {
                                s.next(ifaces.nextElement());
                            } else {
                                s.complete();
                            }
                        }).flatMap(iface -> {
                            if (isMulticastEnabledIPv4Interface(iface)) {
                                return in.join(multicastGroup, iface);
                            }
                            return Flux.empty();
                        }).thenMany(in.receive().asByteArray()).log().subscribe(bytes -> {
                            if (bytes.length == 1024) {
                                latch.countDown();
                            }
                        });
                        return Flux.never();
                    }).block();

            servers.add(server);
        }

        for (int i = 0; i < Schedulers.DEFAULT_POOL_SIZE; i++) {
            threadPool.submit(() -> {
                try {
                    MulticastSocket multicast = new MulticastSocket();
                    multicast.joinGroup(new InetSocketAddress(multicastGroup, port), multicastInterface);

                    byte[] data = new byte[1024];
                    new Random().nextBytes(data);

                    multicast.send(new DatagramPacket(data, data.length, multicastGroup, port));

                    multicast.close();
                } catch (Exception e) {
                    throw new IllegalStateException(e);
                }
            }).get(5, TimeUnit.SECONDS);
        }

        latch.await(5, TimeUnit.SECONDS);
        assertThat("latch was not counted down enough: " + latch.getCount() + " left on " + (4 ^ 2),
                latch.getCount() == 0);

        for (NettyContext s : servers) {
            s.dispose();
        }
    }

    private boolean isMulticastEnabledIPv4Interface(NetworkInterface iface) {
        try {
            if (!iface.supportsMulticast() || !iface.isUp()) {
                return false;
            }
        } catch (SocketException se) {
            return false;
        }

        for (Enumeration<InetAddress> i = iface.getInetAddresses(); i.hasMoreElements();) {
            InetAddress address = i.nextElement();
            if (address.getClass() == Inet4Address.class) {
                return true;
            }
        }

        return false;
    }

    private NetworkInterface findMulticastEnabledIPv4Interface() throws SocketException {
        if (isMulticastEnabledIPv4Interface(NetUtil.LOOPBACK_IF)) {
            return NetUtil.LOOPBACK_IF;
        }

        for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces
                .hasMoreElements();) {
            NetworkInterface iface = ifaces.nextElement();
            if (isMulticastEnabledIPv4Interface(iface)) {
                return iface;
            }
        }

        throw new UnsupportedOperationException(
                "This test requires a multicast enabled IPv4 network interface, but " + "none" + " "
                        + "were found");
    }
}