Package reactor.net.netty.udp

Source Code of reactor.net.netty.udp.NettyDatagramServer$PromiseCompletingListener

/*
* Copyright (c) 2011-2014 Pivotal Software, Inc.
*
*  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.net.netty.udp;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ChannelFactory;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannelConfig;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Environment;
import reactor.core.Reactor;
import reactor.event.dispatch.SynchronousDispatcher;
import reactor.function.Consumer;
import reactor.function.batch.BatchConsumer;
import reactor.io.Buffer;
import reactor.io.encoding.Codec;
import reactor.net.NetChannel;
import reactor.net.config.ServerSocketOptions;
import reactor.net.netty.NettyNetChannel;
import reactor.net.netty.NettyNetChannelInboundHandler;
import reactor.net.netty.NettyServerSocketOptions;
import reactor.net.udp.DatagramServer;
import reactor.rx.Promise;
import reactor.rx.Promises;
import reactor.rx.Stream;
import reactor.support.NamedDaemonThreadFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.util.Collection;

/**
* {@link reactor.net.udp.DatagramServer} implementation built on Netty.
*
* @author Jon Brisbin
* @author Stephane Maldini
*/
public class NettyDatagramServer<IN, OUT> extends DatagramServer<IN, OUT> {

  private final Logger log = LoggerFactory.getLogger(getClass());

  private final    NettyServerSocketOptions nettyOptions;
  private final    Bootstrap                bootstrap;
  private final    EventLoopGroup           ioGroup;
  private volatile NioDatagramChannel       channel;
  private volatile NettyNetChannel<IN, OUT> netChannel;

  public NettyDatagramServer(@Nonnull Environment env,
                             @Nonnull Reactor reactor,
                             @Nullable InetSocketAddress listenAddress,
                             @Nullable final NetworkInterface multicastInterface,
                             @Nonnull final ServerSocketOptions options,
                             @Nullable Codec<Buffer, IN, OUT> codec,
                             Collection<Consumer<NetChannel<IN, OUT>>> consumers) {
    super(env, reactor, listenAddress, multicastInterface, options, codec, consumers);

    if (options instanceof NettyServerSocketOptions) {
      this.nettyOptions = (NettyServerSocketOptions) options;
    } else {
      this.nettyOptions = null;
    }

    if (null != nettyOptions && null != nettyOptions.eventLoopGroup()) {
      this.ioGroup = nettyOptions.eventLoopGroup();
    } else {
      int ioThreadCount = env.getProperty("reactor.udp.ioThreadCount",
                                          Integer.class,
                                          Environment.PROCESSORS);
      this.ioGroup = new NioEventLoopGroup(ioThreadCount, new NamedDaemonThreadFactory("reactor-udp-io"));
    }

    final NettyNetChannelInboundHandler inboundHandler = new NettyNetChannelInboundHandler() {
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        super.channelRead(ctx, ((DatagramPacket) msg).content());
      }
    };

    this.bootstrap = new Bootstrap()
        .group(ioGroup)
        .option(ChannelOption.SO_RCVBUF, options.rcvbuf())
        .option(ChannelOption.SO_SNDBUF, options.sndbuf())
        .option(ChannelOption.SO_REUSEADDR, options.reuseAddr())
        .channelFactory(new ChannelFactory<Channel>() {
          @Override
          public Channel newChannel() {
            final NioDatagramChannel ch = new NioDatagramChannel();
            DatagramChannelConfig config = ch.config();
            config.setReceiveBufferSize(options.rcvbuf());
            config.setSendBufferSize(options.sndbuf());
            config.setReuseAddress(options.reuseAddr());

            if (null != multicastInterface) {
              config.setNetworkInterface(multicastInterface);
            }

            if (null != nettyOptions && null != nettyOptions.pipelineConfigurer()) {
              nettyOptions.pipelineConfigurer().accept(ch.pipeline());
            }

            ch.closeFuture().addListener(new ChannelFutureListener() {
              @Override
              public void operationComplete(ChannelFuture future) throws Exception {
                if (log.isInfoEnabled()) {
                  log.info("CLOSE {}", ch);
                }
                close(ch);
              }
            });

            netChannel = (NettyNetChannel<IN, OUT>) select(ch);
            inboundHandler.setNetChannel(netChannel);

            ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {
              @Override
              public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                super.write(ctx, msg, promise);
              }
            });

            return ch;
          }
        })
        .handler(inboundHandler);

    if (null != listenAddress) {
      bootstrap.localAddress(listenAddress);
    } else {
      bootstrap.localAddress(NetUtil.LOCALHOST, 3000);
    }
    if (null != multicastInterface) {
      bootstrap.option(ChannelOption.IP_MULTICAST_IF, multicastInterface);
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public DatagramServer<IN, OUT> start(@Nullable final Runnable started) {
    ChannelFuture future = bootstrap.bind();
    if (null != started) {
      future.addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
          if (future.isSuccess()) {
            log.info("BIND {}", future.channel().localAddress());
            notifyStart(started);
            channel = (NioDatagramChannel) future.channel();
          }
        }
      });
    }
    return this;
  }

  @Override
  public Promise<Boolean> shutdown() {
    final Promise<Boolean> d = Promises.defer(getEnvironment(), getReactor().getDispatcher());

    getReactor().schedule(
        new Consumer<Void>() {
          @SuppressWarnings("unchecked")
          @Override
          public void accept(Void v) {
            GenericFutureListener listener = new GenericFutureListener() {
              @Override
              public void operationComplete(Future future) throws Exception {
                if (future.isSuccess()) {
                  d.onNext(true);
                } else {
                  d.onError(future.cause());
                }
              }
            };
            if (null == nettyOptions || null == nettyOptions.eventLoopGroup()) {
              ioGroup.shutdownGracefully().addListener(listener);
            }
          }
        },
        null
    );
    notifyShutdown();

    return d;
  }

  @Override
  public DatagramServer<IN, OUT> send(OUT data) {
    if (null == channel) {
      throw new IllegalStateException("DatagramServer not running.");
    }

    netChannel.send(data);

    return this;
  }

  @Override
  public Stream<IN> in() {
    return netChannel.in();
  }

  @Override
  public BatchConsumer<OUT> out() {
    return netChannel.out();
  }

  @Override
  public Promise<Void> join(InetAddress multicastAddress, NetworkInterface iface) {
    if (null == channel) {
      throw new IllegalStateException("DatagramServer not running.");
    }

    final Promise<Void> d = Promises.defer(getEnvironment(), getReactor().getDispatcher());

    if (null == iface && null != getMulticastInterface()) {
      iface = getMulticastInterface();
    }

    final ChannelFuture future;
    if (null != iface) {
      future = channel.joinGroup(new InetSocketAddress(multicastAddress, getListenAddress().getPort()), iface);
    } else {
      future = channel.joinGroup(multicastAddress);
    }
    future.addListener(new PromiseCompletingListener(d));

    return d;
  }

  @Override
  public Promise<Void> leave(InetAddress multicastAddress, NetworkInterface iface) {
    if (null == channel) {
      throw new IllegalStateException("DatagramServer not running.");
    }

    if (null == iface && null != getMulticastInterface()) {
      iface = getMulticastInterface();
    }

    final Promise<Void> d = Promises.defer(getEnvironment(), getReactor().getDispatcher());

    final ChannelFuture future;
    if (null != iface) {
      future = channel.leaveGroup(new InetSocketAddress(multicastAddress, getListenAddress().getPort()), iface);
    } else {
      future = channel.leaveGroup(multicastAddress);
    }
    future.addListener(new PromiseCompletingListener(d));

    return d;
  }

  @Override
  protected <C> NetChannel<IN, OUT> createChannel(C ioChannel) {
    return new NettyNetChannel<IN, OUT>(
        getEnvironment(),
        getCodec(),
        SynchronousDispatcher.INSTANCE,
        getReactor(),
        (NioDatagramChannel) ioChannel
    );
  }

  @Override
  protected void doClose(@Nullable final Consumer<Boolean> onClose) {
    ChannelFuture future = channel.close();
    if (null != onClose) {
      future.addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
          getReactor().schedule(onClose, future.isDone() && future.isSuccess());
        }
      });
    }
  }

  private static class PromiseCompletingListener implements ChannelFutureListener {
    private final Promise<Void> d;

    private PromiseCompletingListener(Promise<Void> d) {
      this.d = d;
    }

    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
      if (future.isSuccess()) {
        d.onComplete();
      } else {
        d.onError(future.cause());
      }
    }
  }

}
TOP

Related Classes of reactor.net.netty.udp.NettyDatagramServer$PromiseCompletingListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.